Merged pull request #12 from bhy/T423.
[cython.git] / Cython / Compiler / UtilNodes.py
1 #
2 # Nodes used as utilities and support for transforms etc.
3 # These often make up sets including both Nodes and ExprNodes
4 # so it is convenient to have them in a seperate module.
5 #
6
7 import Nodes
8 import ExprNodes
9 from Nodes import Node
10 from ExprNodes import AtomicExprNode
11 from PyrexTypes import c_ptr_type
12
13 class TempHandle(object):
14     # THIS IS DEPRECATED, USE LetRefNode instead
15     temp = None
16     needs_xdecref = False
17     def __init__(self, type):
18         self.type = type
19         self.needs_cleanup = type.is_pyobject
20
21     def ref(self, pos):
22         return TempRefNode(pos, handle=self, type=self.type)
23
24     def cleanup_ref(self, pos):
25         return CleanupTempRefNode(pos, handle=self, type=self.type)
26
27 class TempRefNode(AtomicExprNode):
28     # THIS IS DEPRECATED, USE LetRefNode instead
29     # handle   TempHandle
30
31     def analyse_types(self, env):
32         assert self.type == self.handle.type
33
34     def analyse_target_types(self, env):
35         assert self.type == self.handle.type
36
37     def analyse_target_declaration(self, env):
38         pass
39
40     def calculate_result_code(self):
41         result = self.handle.temp
42         if result is None: result = "<error>" # might be called and overwritten
43         return result
44
45     def generate_result_code(self, code):
46         pass
47
48     def generate_assignment_code(self, rhs, code):
49         if self.type.is_pyobject:
50             rhs.make_owned_reference(code)
51             # TODO: analyse control flow to see if this is necessary
52             code.put_xdecref(self.result(), self.ctype())
53         code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype())))
54         rhs.generate_post_assignment_code(code)
55         rhs.free_temps(code)
56
57 class CleanupTempRefNode(TempRefNode):
58     # THIS IS DEPRECATED, USE LetRefNode instead
59     # handle   TempHandle
60
61     def generate_assignment_code(self, rhs, code):
62         pass
63
64     def generate_execution_code(self, code):
65         if self.type.is_pyobject:
66             code.put_decref_clear(self.result(), self.type)
67             self.handle.needs_cleanup = False
68
69 class TempsBlockNode(Node):
70     # THIS IS DEPRECATED, USE LetNode instead
71
72     """
73     Creates a block which allocates temporary variables.
74     This is used by transforms to output constructs that need
75     to make use of a temporary variable. Simply pass the types
76     of the needed temporaries to the constructor.
77
78     The variables can be referred to using a TempRefNode
79     (which can be constructed by calling get_ref_node).
80     """
81
82     # temps   [TempHandle]
83     # body    StatNode
84
85     child_attrs = ["body"]
86
87     def generate_execution_code(self, code):
88         for handle in self.temps:
89             handle.temp = code.funcstate.allocate_temp(
90                 handle.type, manage_ref=handle.needs_cleanup)
91         self.body.generate_execution_code(code)
92         for handle in self.temps:
93             if handle.needs_cleanup:
94                 if handle.needs_xdecref:
95                     code.put_xdecref_clear(handle.temp, handle.type)
96                 else:
97                     code.put_decref_clear(handle.temp, handle.type)
98             code.funcstate.release_temp(handle.temp)
99
100     def analyse_control_flow(self, env):
101         self.body.analyse_control_flow(env)
102
103     def analyse_declarations(self, env):
104         self.body.analyse_declarations(env)
105
106     def analyse_expressions(self, env):
107         self.body.analyse_expressions(env)
108
109     def generate_function_definitions(self, env, code):
110         self.body.generate_function_definitions(env, code)
111
112     def annotate(self, code):
113         self.body.annotate(code)
114
115
116 class ResultRefNode(AtomicExprNode):
117     # A reference to the result of an expression.  The result_code
118     # must be set externally (usually a temp name).
119
120     subexprs = []
121     lhs_of_first_assignment = False
122
123     def __init__(self, expression=None, pos=None, type=None, may_hold_none=True):
124         self.expression = expression
125         self.pos = None
126         self.may_hold_none = may_hold_none
127         if expression is not None:
128             self.pos = expression.pos
129             if hasattr(expression, "type"):
130                 self.type = expression.type
131         if pos is not None:
132             self.pos = pos
133         if type is not None:
134             self.type = type
135         assert self.pos is not None
136
137     def clone_node(self):
138         # nothing to do here
139         return self
140
141     def analyse_types(self, env):
142         if self.expression is not None:
143             self.type = self.expression.type
144
145     def infer_type(self, env):
146         if self.expression is not None:
147             return self.expression.infer_type(env)
148         if self.type is not None:
149             return self.type
150         assert False, "cannot infer type of ResultRefNode"
151
152     def may_be_none(self):
153         if not self.type.is_pyobject:
154             return False
155         return self.may_hold_none
156
157     def _DISABLED_may_be_none(self):
158         # not sure if this is safe - the expression may not be the
159         # only value that gets assigned
160         if self.expression is not None:
161             return self.expression.may_be_none()
162         if self.type is not None:
163             return self.type.is_pyobject
164         return True # play safe
165
166     def is_simple(self):
167         return True
168
169     def result(self):
170         try:
171             return self.result_code
172         except AttributeError:
173             if self.expression is not None:
174                 self.result_code = self.expression.result()
175         return self.result_code
176
177     def generate_evaluation_code(self, code):
178         pass
179
180     def generate_result_code(self, code):
181         pass
182
183     def generate_disposal_code(self, code):
184         pass
185
186     def generate_assignment_code(self, rhs, code):
187         if self.type.is_pyobject:
188             rhs.make_owned_reference(code)
189             if not self.lhs_of_first_assignment:
190                 code.put_decref(self.result(), self.ctype())
191         code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype())))
192         rhs.generate_post_assignment_code(code)
193         rhs.free_temps(code)
194
195     def allocate_temps(self, env):
196         pass
197
198     def release_temp(self, env):
199         pass
200
201     def free_temps(self, code):
202         pass
203
204
205 class LetNodeMixin:
206     def set_temp_expr(self, lazy_temp):
207         self.lazy_temp = lazy_temp
208         self.temp_expression = lazy_temp.expression
209
210     def setup_temp_expr(self, code):
211         self.temp_expression.generate_evaluation_code(code)
212         self.temp_type = self.temp_expression.type
213         if self.temp_type.is_array:
214             self.temp_type = c_ptr_type(self.temp_type.base_type)
215         self._result_in_temp = self.temp_expression.result_in_temp()
216         if self._result_in_temp:
217             self.temp = self.temp_expression.result()
218         else:
219             self.temp_expression.make_owned_reference(code)
220             self.temp = code.funcstate.allocate_temp(
221                 self.temp_type, manage_ref=True)
222             code.putln("%s = %s;" % (self.temp, self.temp_expression.result()))
223             self.temp_expression.generate_disposal_code(code)
224             self.temp_expression.free_temps(code)
225         self.lazy_temp.result_code = self.temp
226
227     def teardown_temp_expr(self, code):
228         if self._result_in_temp:
229             self.temp_expression.generate_disposal_code(code)
230             self.temp_expression.free_temps(code)
231         else:
232             if self.temp_type.is_pyobject:
233                 code.put_decref_clear(self.temp, self.temp_type)
234             code.funcstate.release_temp(self.temp)
235
236 class EvalWithTempExprNode(ExprNodes.ExprNode, LetNodeMixin):
237     # A wrapper around a subexpression that moves an expression into a
238     # temp variable and provides it to the subexpression.
239
240     subexprs = ['temp_expression', 'subexpression']
241
242     def __init__(self, lazy_temp, subexpression):
243         self.set_temp_expr(lazy_temp)
244         self.pos = subexpression.pos
245         self.subexpression = subexpression
246         # if called after type analysis, we already know the type here
247         self.type = self.subexpression.type
248
249     def infer_type(self, env):
250         return self.subexpression.infer_type(env)
251
252     def result(self):
253         return self.subexpression.result()
254
255     def analyse_types(self, env):
256         self.temp_expression.analyse_types(env)
257         self.subexpression.analyse_types(env)
258         self.type = self.subexpression.type
259
260     def free_subexpr_temps(self, code):
261         self.subexpression.free_temps(code)
262
263     def generate_subexpr_disposal_code(self, code):
264         self.subexpression.generate_disposal_code(code)
265
266     def generate_evaluation_code(self, code):
267         self.setup_temp_expr(code)
268         self.subexpression.generate_evaluation_code(code)
269         self.teardown_temp_expr(code)
270
271 LetRefNode = ResultRefNode
272
273 class LetNode(Nodes.StatNode, LetNodeMixin):
274     # Implements a local temporary variable scope. Imagine this
275     # syntax being present:
276     # let temp = VALUE:
277     #     BLOCK (can modify temp)
278     #     if temp is an object, decref
279     #
280     # Usually used after analysis phase, but forwards analysis methods
281     # to its children
282
283     child_attrs = ['temp_expression', 'body']
284
285     def __init__(self, lazy_temp, body):
286         self.set_temp_expr(lazy_temp)
287         self.pos = body.pos
288         self.body = body
289
290     def analyse_control_flow(self, env):
291         self.body.analyse_control_flow(env)
292
293     def analyse_declarations(self, env):
294         self.temp_expression.analyse_declarations(env)
295         self.body.analyse_declarations(env)
296
297     def analyse_expressions(self, env):
298         self.temp_expression.analyse_expressions(env)
299         self.body.analyse_expressions(env)
300
301     def generate_execution_code(self, code):
302         self.setup_temp_expr(code)
303         self.body.generate_execution_code(code)
304         self.teardown_temp_expr(code)
305
306 class TempResultFromStatNode(ExprNodes.ExprNode):
307     # An ExprNode wrapper around a StatNode that executes the StatNode
308     # body.  Requires a ResultRefNode that it sets up to refer to its
309     # own temp result.  The StatNode must assign a value to the result
310     # node, which then becomes the result of this node.
311
312     subexprs = []
313     child_attrs = ['body']
314
315     def __init__(self, result_ref, body):
316         self.result_ref = result_ref
317         self.pos = body.pos
318         self.body = body
319         self.type = result_ref.type
320         self.is_temp = 1
321
322     def analyse_declarations(self, env):
323         self.body.analyse_declarations(env)
324
325     def analyse_types(self, env):
326         self.body.analyse_expressions(env)
327
328     def generate_result_code(self, code):
329         self.result_ref.result_code = self.result()
330         self.body.generate_execution_code(code)