Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / third_party / cython / src / Cython / Compiler / FlowControl.py
bloba36ffa467614567ef8a3dae747f71abe2331b594
1 import cython
2 cython.declare(PyrexTypes=object, ExprNodes=object, Nodes=object,
3 Builtin=object, InternalError=object,
4 error=object, warning=object,
5 py_object_type=object, unspecified_type=object,
6 object_expr=object, object_expr_not_none=object,
7 fake_rhs_expr=object, TypedExprNode=object)
9 import Builtin
10 import ExprNodes
11 import Nodes
12 import Options
13 from PyrexTypes import py_object_type, unspecified_type
14 import PyrexTypes
16 from Visitor import TreeVisitor, CythonTransform
17 from Errors import error, warning, InternalError
19 class TypedExprNode(ExprNodes.ExprNode):
20 # Used for declaring assignments of a specified type without a known entry.
21 def __init__(self, type, may_be_none=None, pos=None):
22 super(TypedExprNode, self).__init__(pos)
23 self.type = type
24 self._may_be_none = may_be_none
26 def may_be_none(self):
27 return self._may_be_none != False
29 object_expr = TypedExprNode(py_object_type, may_be_none=True)
30 object_expr_not_none = TypedExprNode(py_object_type, may_be_none=False)
31 # Fake rhs to silence "unused variable" warning
32 fake_rhs_expr = TypedExprNode(unspecified_type)
35 class ControlBlock(object):
36 """Control flow graph node. Sequence of assignments and name references.
38 children set of children nodes
39 parents set of parent nodes
40 positions set of position markers
42 stats list of block statements
43 gen dict of assignments generated by this block
44 bounded set of entries that are definitely bounded in this block
46 Example:
48 a = 1
49 b = a + c # 'c' is already bounded or exception here
51 stats = [Assignment(a), NameReference(a), NameReference(c),
52 Assignment(b)]
53 gen = {Entry(a): Assignment(a), Entry(b): Assignment(b)}
54 bounded = set([Entry(a), Entry(c)])
56 """
58 def __init__(self):
59 self.children = set()
60 self.parents = set()
61 self.positions = set()
63 self.stats = []
64 self.gen = {}
65 self.bounded = set()
67 self.i_input = 0
68 self.i_output = 0
69 self.i_gen = 0
70 self.i_kill = 0
71 self.i_state = 0
73 def empty(self):
74 return (not self.stats and not self.positions)
76 def detach(self):
77 """Detach block from parents and children."""
78 for child in self.children:
79 child.parents.remove(self)
80 for parent in self.parents:
81 parent.children.remove(self)
82 self.parents.clear()
83 self.children.clear()
85 def add_child(self, block):
86 self.children.add(block)
87 block.parents.add(self)
90 class ExitBlock(ControlBlock):
91 """Non-empty exit point block."""
93 def empty(self):
94 return False
97 class AssignmentList(object):
98 def __init__(self):
99 self.stats = []
102 class ControlFlow(object):
103 """Control-flow graph.
105 entry_point ControlBlock entry point for this graph
106 exit_point ControlBlock normal exit point
107 block ControlBlock current block
108 blocks set children nodes
109 entries set tracked entries
110 loops list stack for loop descriptors
111 exceptions list stack for exception descriptors
114 def __init__(self):
115 self.blocks = set()
116 self.entries = set()
117 self.loops = []
118 self.exceptions = []
120 self.entry_point = ControlBlock()
121 self.exit_point = ExitBlock()
122 self.blocks.add(self.exit_point)
123 self.block = self.entry_point
125 def newblock(self, parent=None):
126 """Create floating block linked to `parent` if given.
128 NOTE: Block is NOT added to self.blocks
130 block = ControlBlock()
131 self.blocks.add(block)
132 if parent:
133 parent.add_child(block)
134 return block
136 def nextblock(self, parent=None):
137 """Create block children block linked to current or `parent` if given.
139 NOTE: Block is added to self.blocks
141 block = ControlBlock()
142 self.blocks.add(block)
143 if parent:
144 parent.add_child(block)
145 elif self.block:
146 self.block.add_child(block)
147 self.block = block
148 return self.block
150 def is_tracked(self, entry):
151 if entry.is_anonymous:
152 return False
153 return (entry.is_local or entry.is_pyclass_attr or entry.is_arg or
154 entry.from_closure or entry.in_closure or
155 entry.error_on_uninitialized)
157 def is_statically_assigned(self, entry):
158 if (entry.is_local and entry.is_variable and
159 (entry.type.is_struct_or_union or
160 entry.type.is_complex or
161 entry.type.is_array or
162 entry.type.is_cpp_class)):
163 # stack allocated structured variable => never uninitialised
164 return True
165 return False
167 def mark_position(self, node):
168 """Mark position, will be used to draw graph nodes."""
169 if self.block:
170 self.block.positions.add(node.pos[:2])
172 def mark_assignment(self, lhs, rhs, entry):
173 if self.block and self.is_tracked(entry):
174 assignment = NameAssignment(lhs, rhs, entry)
175 self.block.stats.append(assignment)
176 self.block.gen[entry] = assignment
177 self.entries.add(entry)
179 def mark_argument(self, lhs, rhs, entry):
180 if self.block and self.is_tracked(entry):
181 assignment = Argument(lhs, rhs, entry)
182 self.block.stats.append(assignment)
183 self.block.gen[entry] = assignment
184 self.entries.add(entry)
186 def mark_deletion(self, node, entry):
187 if self.block and self.is_tracked(entry):
188 assignment = NameDeletion(node, entry)
189 self.block.stats.append(assignment)
190 self.block.gen[entry] = Uninitialized
191 self.entries.add(entry)
193 def mark_reference(self, node, entry):
194 if self.block and self.is_tracked(entry):
195 self.block.stats.append(NameReference(node, entry))
196 ## XXX: We don't track expression evaluation order so we can't use
197 ## XXX: successful reference as initialization sign.
198 ## # Local variable is definitely bound after this reference
199 ## if not node.allow_null:
200 ## self.block.bounded.add(entry)
201 self.entries.add(entry)
203 def normalize(self):
204 """Delete unreachable and orphan blocks."""
205 queue = set([self.entry_point])
206 visited = set()
207 while queue:
208 root = queue.pop()
209 visited.add(root)
210 for child in root.children:
211 if child not in visited:
212 queue.add(child)
213 unreachable = self.blocks - visited
214 for block in unreachable:
215 block.detach()
216 visited.remove(self.entry_point)
217 for block in visited:
218 if block.empty():
219 for parent in block.parents: # Re-parent
220 for child in block.children:
221 parent.add_child(child)
222 block.detach()
223 unreachable.add(block)
224 self.blocks -= unreachable
226 def initialize(self):
227 """Set initial state, map assignments to bits."""
228 self.assmts = {}
230 bit = 1
231 for entry in self.entries:
232 assmts = AssignmentList()
233 assmts.mask = assmts.bit = bit
234 self.assmts[entry] = assmts
235 bit <<= 1
237 for block in self.blocks:
238 for stat in block.stats:
239 if isinstance(stat, NameAssignment):
240 stat.bit = bit
241 assmts = self.assmts[stat.entry]
242 assmts.stats.append(stat)
243 assmts.mask |= bit
244 bit <<= 1
246 for block in self.blocks:
247 for entry, stat in block.gen.items():
248 assmts = self.assmts[entry]
249 if stat is Uninitialized:
250 block.i_gen |= assmts.bit
251 else:
252 block.i_gen |= stat.bit
253 block.i_kill |= assmts.mask
254 block.i_output = block.i_gen
255 for entry in block.bounded:
256 block.i_kill |= self.assmts[entry].bit
258 for assmts in self.assmts.itervalues():
259 self.entry_point.i_gen |= assmts.bit
260 self.entry_point.i_output = self.entry_point.i_gen
262 def map_one(self, istate, entry):
263 ret = set()
264 assmts = self.assmts[entry]
265 if istate & assmts.bit:
266 if self.is_statically_assigned(entry):
267 ret.add(StaticAssignment(entry))
268 elif entry.from_closure:
269 ret.add(Unknown)
270 else:
271 ret.add(Uninitialized)
272 for assmt in assmts.stats:
273 if istate & assmt.bit:
274 ret.add(assmt)
275 return ret
277 def reaching_definitions(self):
278 """Per-block reaching definitions analysis."""
279 dirty = True
280 while dirty:
281 dirty = False
282 for block in self.blocks:
283 i_input = 0
284 for parent in block.parents:
285 i_input |= parent.i_output
286 i_output = (i_input & ~block.i_kill) | block.i_gen
287 if i_output != block.i_output:
288 dirty = True
289 block.i_input = i_input
290 block.i_output = i_output
293 class LoopDescr(object):
294 def __init__(self, next_block, loop_block):
295 self.next_block = next_block
296 self.loop_block = loop_block
297 self.exceptions = []
300 class ExceptionDescr(object):
301 """Exception handling helper.
303 entry_point ControlBlock Exception handling entry point
304 finally_enter ControlBlock Normal finally clause entry point
305 finally_exit ControlBlock Normal finally clause exit point
308 def __init__(self, entry_point, finally_enter=None, finally_exit=None):
309 self.entry_point = entry_point
310 self.finally_enter = finally_enter
311 self.finally_exit = finally_exit
314 class NameAssignment(object):
315 def __init__(self, lhs, rhs, entry):
316 if lhs.cf_state is None:
317 lhs.cf_state = set()
318 self.lhs = lhs
319 self.rhs = rhs
320 self.entry = entry
321 self.pos = lhs.pos
322 self.refs = set()
323 self.is_arg = False
324 self.is_deletion = False
325 self.inferred_type = None
327 def __repr__(self):
328 return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
330 def infer_type(self):
331 self.inferred_type = self.rhs.infer_type(self.entry.scope)
332 return self.inferred_type
334 def type_dependencies(self):
335 return self.rhs.type_dependencies(self.entry.scope)
337 @property
338 def type(self):
339 if not self.entry.type.is_unspecified:
340 return self.entry.type
341 return self.inferred_type
344 class StaticAssignment(NameAssignment):
345 """Initialised at declaration time, e.g. stack allocation."""
346 def __init__(self, entry):
347 if not entry.type.is_pyobject:
348 may_be_none = False
349 else:
350 may_be_none = None # unknown
351 lhs = TypedExprNode(
352 entry.type, may_be_none=may_be_none, pos=entry.pos)
353 super(StaticAssignment, self).__init__(lhs, lhs, entry)
355 def infer_type(self):
356 return self.entry.type
358 def type_dependencies(self):
359 return ()
362 class Argument(NameAssignment):
363 def __init__(self, lhs, rhs, entry):
364 NameAssignment.__init__(self, lhs, rhs, entry)
365 self.is_arg = True
368 class NameDeletion(NameAssignment):
369 def __init__(self, lhs, entry):
370 NameAssignment.__init__(self, lhs, lhs, entry)
371 self.is_deletion = True
373 def infer_type(self):
374 inferred_type = self.rhs.infer_type(self.entry.scope)
375 if (not inferred_type.is_pyobject and
376 inferred_type.can_coerce_to_pyobject(self.entry.scope)):
377 return py_object_type
378 self.inferred_type = inferred_type
379 return inferred_type
382 class Uninitialized(object):
383 """Definitely not initialised yet."""
386 class Unknown(object):
387 """Coming from outer closure, might be initialised or not."""
390 class NameReference(object):
391 def __init__(self, node, entry):
392 if node.cf_state is None:
393 node.cf_state = set()
394 self.node = node
395 self.entry = entry
396 self.pos = node.pos
398 def __repr__(self):
399 return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
402 class ControlFlowState(list):
403 # Keeps track of Node's entry assignments
405 # cf_is_null [boolean] It is uninitialized
406 # cf_maybe_null [boolean] May be uninitialized
407 # is_single [boolean] Has only one assignment at this point
409 cf_maybe_null = False
410 cf_is_null = False
411 is_single = False
413 def __init__(self, state):
414 if Uninitialized in state:
415 state.discard(Uninitialized)
416 self.cf_maybe_null = True
417 if not state:
418 self.cf_is_null = True
419 elif Unknown in state:
420 state.discard(Unknown)
421 self.cf_maybe_null = True
422 else:
423 if len(state) == 1:
424 self.is_single = True
425 # XXX: Remove fake_rhs_expr
426 super(ControlFlowState, self).__init__(
427 [i for i in state if i.rhs is not fake_rhs_expr])
429 def one(self):
430 return self[0]
433 class GVContext(object):
434 """Graphviz subgraph object."""
436 def __init__(self):
437 self.blockids = {}
438 self.nextid = 0
439 self.children = []
440 self.sources = {}
442 def add(self, child):
443 self.children.append(child)
445 def nodeid(self, block):
446 if block not in self.blockids:
447 self.blockids[block] = 'block%d' % self.nextid
448 self.nextid += 1
449 return self.blockids[block]
451 def extract_sources(self, block):
452 if not block.positions:
453 return ''
454 start = min(block.positions)
455 stop = max(block.positions)
456 srcdescr = start[0]
457 if not srcdescr in self.sources:
458 self.sources[srcdescr] = list(srcdescr.get_lines())
459 lines = self.sources[srcdescr]
460 return '\\n'.join([l.strip() for l in lines[start[1] - 1:stop[1]]])
462 def render(self, fp, name, annotate_defs=False):
463 """Render graphviz dot graph"""
464 fp.write('digraph %s {\n' % name)
465 fp.write(' node [shape=box];\n')
466 for child in self.children:
467 child.render(fp, self, annotate_defs)
468 fp.write('}\n')
470 def escape(self, text):
471 return text.replace('"', '\\"').replace('\n', '\\n')
474 class GV(object):
475 """Graphviz DOT renderer."""
477 def __init__(self, name, flow):
478 self.name = name
479 self.flow = flow
481 def render(self, fp, ctx, annotate_defs=False):
482 fp.write(' subgraph %s {\n' % self.name)
483 for block in self.flow.blocks:
484 label = ctx.extract_sources(block)
485 if annotate_defs:
486 for stat in block.stats:
487 if isinstance(stat, NameAssignment):
488 label += '\n %s [definition]' % stat.entry.name
489 elif isinstance(stat, NameReference):
490 if stat.entry:
491 label += '\n %s [reference]' % stat.entry.name
492 if not label:
493 label = 'empty'
494 pid = ctx.nodeid(block)
495 fp.write(' %s [label="%s"];\n' % (pid, ctx.escape(label)))
496 for block in self.flow.blocks:
497 pid = ctx.nodeid(block)
498 for child in block.children:
499 fp.write(' %s -> %s;\n' % (pid, ctx.nodeid(child)))
500 fp.write(' }\n')
503 class MessageCollection(object):
504 """Collect error/warnings messages first then sort"""
505 def __init__(self):
506 self.messages = []
508 def error(self, pos, message):
509 self.messages.append((pos, True, message))
511 def warning(self, pos, message):
512 self.messages.append((pos, False, message))
514 def report(self):
515 self.messages.sort()
516 for pos, is_error, message in self.messages:
517 if is_error:
518 error(pos, message)
519 else:
520 warning(pos, message, 2)
523 def check_definitions(flow, compiler_directives):
524 flow.initialize()
525 flow.reaching_definitions()
527 # Track down state
528 assignments = set()
529 # Node to entry map
530 references = {}
531 assmt_nodes = set()
533 for block in flow.blocks:
534 i_state = block.i_input
535 for stat in block.stats:
536 i_assmts = flow.assmts[stat.entry]
537 state = flow.map_one(i_state, stat.entry)
538 if isinstance(stat, NameAssignment):
539 stat.lhs.cf_state.update(state)
540 assmt_nodes.add(stat.lhs)
541 i_state = i_state & ~i_assmts.mask
542 if stat.is_deletion:
543 i_state |= i_assmts.bit
544 else:
545 i_state |= stat.bit
546 assignments.add(stat)
547 if stat.rhs is not fake_rhs_expr:
548 stat.entry.cf_assignments.append(stat)
549 elif isinstance(stat, NameReference):
550 references[stat.node] = stat.entry
551 stat.entry.cf_references.append(stat)
552 stat.node.cf_state.update(state)
553 ## if not stat.node.allow_null:
554 ## i_state &= ~i_assmts.bit
555 ## # after successful read, the state is known to be initialised
556 state.discard(Uninitialized)
557 state.discard(Unknown)
558 for assmt in state:
559 assmt.refs.add(stat)
561 # Check variable usage
562 warn_maybe_uninitialized = compiler_directives['warn.maybe_uninitialized']
563 warn_unused_result = compiler_directives['warn.unused_result']
564 warn_unused = compiler_directives['warn.unused']
565 warn_unused_arg = compiler_directives['warn.unused_arg']
567 messages = MessageCollection()
569 # assignment hints
570 for node in assmt_nodes:
571 if Uninitialized in node.cf_state:
572 node.cf_maybe_null = True
573 if len(node.cf_state) == 1:
574 node.cf_is_null = True
575 else:
576 node.cf_is_null = False
577 elif Unknown in node.cf_state:
578 node.cf_maybe_null = True
579 else:
580 node.cf_is_null = False
581 node.cf_maybe_null = False
583 # Find uninitialized references and cf-hints
584 for node, entry in references.iteritems():
585 if Uninitialized in node.cf_state:
586 node.cf_maybe_null = True
587 if not entry.from_closure and len(node.cf_state) == 1:
588 node.cf_is_null = True
589 if (node.allow_null or entry.from_closure
590 or entry.is_pyclass_attr or entry.type.is_error):
591 pass # Can be uninitialized here
592 elif node.cf_is_null:
593 if entry.error_on_uninitialized or (
594 Options.error_on_uninitialized and (
595 entry.type.is_pyobject or entry.type.is_unspecified)):
596 messages.error(
597 node.pos,
598 "local variable '%s' referenced before assignment"
599 % entry.name)
600 else:
601 messages.warning(
602 node.pos,
603 "local variable '%s' referenced before assignment"
604 % entry.name)
605 elif warn_maybe_uninitialized:
606 messages.warning(
607 node.pos,
608 "local variable '%s' might be referenced before assignment"
609 % entry.name)
610 elif Unknown in node.cf_state:
611 # TODO: better cross-closure analysis to know when inner functions
612 # are being called before a variable is being set, and when
613 # a variable is known to be set before even defining the
614 # inner function, etc.
615 node.cf_maybe_null = True
616 else:
617 node.cf_is_null = False
618 node.cf_maybe_null = False
620 # Unused result
621 for assmt in assignments:
622 if (not assmt.refs and not assmt.entry.is_pyclass_attr
623 and not assmt.entry.in_closure):
624 if assmt.entry.cf_references and warn_unused_result:
625 if assmt.is_arg:
626 messages.warning(assmt.pos, "Unused argument value '%s'" %
627 assmt.entry.name)
628 else:
629 messages.warning(assmt.pos, "Unused result in '%s'" %
630 assmt.entry.name)
631 assmt.lhs.cf_used = False
633 # Unused entries
634 for entry in flow.entries:
635 if (not entry.cf_references
636 and not entry.is_pyclass_attr):
637 if entry.name != '_':
638 # '_' is often used for unused variables, e.g. in loops
639 if entry.is_arg:
640 if warn_unused_arg:
641 messages.warning(entry.pos, "Unused argument '%s'" %
642 entry.name)
643 else:
644 if warn_unused:
645 messages.warning(entry.pos, "Unused entry '%s'" %
646 entry.name)
647 entry.cf_used = False
649 messages.report()
651 for node in assmt_nodes:
652 node.cf_state = ControlFlowState(node.cf_state)
653 for node in references:
654 node.cf_state = ControlFlowState(node.cf_state)
657 class AssignmentCollector(TreeVisitor):
658 def __init__(self):
659 super(AssignmentCollector, self).__init__()
660 self.assignments = []
662 def visit_Node(self):
663 self._visitchildren(self, None)
665 def visit_SingleAssignmentNode(self, node):
666 self.assignments.append((node.lhs, node.rhs))
668 def visit_CascadedAssignmentNode(self, node):
669 for lhs in node.lhs_list:
670 self.assignments.append((lhs, node.rhs))
673 class ControlFlowAnalysis(CythonTransform):
675 def visit_ModuleNode(self, node):
676 self.gv_ctx = GVContext()
678 # Set of NameNode reductions
679 self.reductions = set()
681 self.in_inplace_assignment = False
682 self.env_stack = []
683 self.env = node.scope
684 self.stack = []
685 self.flow = ControlFlow()
686 self.visitchildren(node)
688 check_definitions(self.flow, self.current_directives)
690 dot_output = self.current_directives['control_flow.dot_output']
691 if dot_output:
692 annotate_defs = self.current_directives['control_flow.dot_annotate_defs']
693 fp = open(dot_output, 'wt')
694 try:
695 self.gv_ctx.render(fp, 'module', annotate_defs=annotate_defs)
696 finally:
697 fp.close()
698 return node
700 def visit_FuncDefNode(self, node):
701 for arg in node.args:
702 if arg.default:
703 self.visitchildren(arg)
704 self.visitchildren(node, ('decorators',))
705 self.env_stack.append(self.env)
706 self.env = node.local_scope
707 self.stack.append(self.flow)
708 self.flow = ControlFlow()
710 # Collect all entries
711 for entry in node.local_scope.entries.values():
712 if self.flow.is_tracked(entry):
713 self.flow.entries.add(entry)
715 self.mark_position(node)
716 # Function body block
717 self.flow.nextblock()
719 for arg in node.args:
720 self._visit(arg)
721 if node.star_arg:
722 self.flow.mark_argument(node.star_arg,
723 TypedExprNode(Builtin.tuple_type,
724 may_be_none=False),
725 node.star_arg.entry)
726 if node.starstar_arg:
727 self.flow.mark_argument(node.starstar_arg,
728 TypedExprNode(Builtin.dict_type,
729 may_be_none=False),
730 node.starstar_arg.entry)
731 self._visit(node.body)
732 # Workaround for generators
733 if node.is_generator:
734 self._visit(node.gbody.body)
736 # Exit point
737 if self.flow.block:
738 self.flow.block.add_child(self.flow.exit_point)
740 # Cleanup graph
741 self.flow.normalize()
742 check_definitions(self.flow, self.current_directives)
743 self.flow.blocks.add(self.flow.entry_point)
745 self.gv_ctx.add(GV(node.local_scope.name, self.flow))
747 self.flow = self.stack.pop()
748 self.env = self.env_stack.pop()
749 return node
751 def visit_DefNode(self, node):
752 node.used = True
753 return self.visit_FuncDefNode(node)
755 def visit_GeneratorBodyDefNode(self, node):
756 return node
758 def visit_CTypeDefNode(self, node):
759 return node
761 def mark_assignment(self, lhs, rhs=None):
762 if not self.flow.block:
763 return
764 if self.flow.exceptions:
765 exc_descr = self.flow.exceptions[-1]
766 self.flow.block.add_child(exc_descr.entry_point)
767 self.flow.nextblock()
769 if not rhs:
770 rhs = object_expr
771 if lhs.is_name:
772 if lhs.entry is not None:
773 entry = lhs.entry
774 else:
775 entry = self.env.lookup(lhs.name)
776 if entry is None: # TODO: This shouldn't happen...
777 return
778 self.flow.mark_assignment(lhs, rhs, entry)
779 elif isinstance(lhs, ExprNodes.SequenceNode):
780 for arg in lhs.args:
781 self.mark_assignment(arg)
782 else:
783 self._visit(lhs)
785 if self.flow.exceptions:
786 exc_descr = self.flow.exceptions[-1]
787 self.flow.block.add_child(exc_descr.entry_point)
788 self.flow.nextblock()
790 def mark_position(self, node):
791 """Mark position if DOT output is enabled."""
792 if self.current_directives['control_flow.dot_output']:
793 self.flow.mark_position(node)
795 def visit_FromImportStatNode(self, node):
796 for name, target in node.items:
797 if name != "*":
798 self.mark_assignment(target)
799 self.visitchildren(node)
800 return node
802 def visit_AssignmentNode(self, node):
803 raise InternalError("Unhandled assignment node")
805 def visit_SingleAssignmentNode(self, node):
806 self._visit(node.rhs)
807 self.mark_assignment(node.lhs, node.rhs)
808 return node
810 def visit_CascadedAssignmentNode(self, node):
811 self._visit(node.rhs)
812 for lhs in node.lhs_list:
813 self.mark_assignment(lhs, node.rhs)
814 return node
816 def visit_ParallelAssignmentNode(self, node):
817 collector = AssignmentCollector()
818 collector.visitchildren(node)
819 for lhs, rhs in collector.assignments:
820 self._visit(rhs)
821 for lhs, rhs in collector.assignments:
822 self.mark_assignment(lhs, rhs)
823 return node
825 def visit_InPlaceAssignmentNode(self, node):
826 self.in_inplace_assignment = True
827 self.visitchildren(node)
828 self.in_inplace_assignment = False
829 self.mark_assignment(node.lhs, node.create_binop_node())
830 return node
832 def visit_DelStatNode(self, node):
833 for arg in node.args:
834 if arg.is_name:
835 entry = arg.entry or self.env.lookup(arg.name)
836 if entry.in_closure or entry.from_closure:
837 error(arg.pos,
838 "can not delete variable '%s' "
839 "referenced in nested scope" % entry.name)
840 # Mark reference
841 self._visit(arg)
842 self.flow.mark_deletion(arg, entry)
843 else:
844 self._visit(arg)
845 return node
847 def visit_CArgDeclNode(self, node):
848 entry = self.env.lookup(node.name)
849 if entry:
850 may_be_none = not node.not_none
851 self.flow.mark_argument(
852 node, TypedExprNode(entry.type, may_be_none), entry)
853 return node
855 def visit_NameNode(self, node):
856 if self.flow.block:
857 entry = node.entry or self.env.lookup(node.name)
858 if entry:
859 self.flow.mark_reference(node, entry)
861 if entry in self.reductions and not self.in_inplace_assignment:
862 error(node.pos,
863 "Cannot read reduction variable in loop body")
865 return node
867 def visit_StatListNode(self, node):
868 if self.flow.block:
869 for stat in node.stats:
870 self._visit(stat)
871 if not self.flow.block:
872 stat.is_terminator = True
873 break
874 return node
876 def visit_Node(self, node):
877 self.visitchildren(node)
878 self.mark_position(node)
879 return node
881 def visit_IfStatNode(self, node):
882 next_block = self.flow.newblock()
883 parent = self.flow.block
884 # If clauses
885 for clause in node.if_clauses:
886 parent = self.flow.nextblock(parent)
887 self._visit(clause.condition)
888 self.flow.nextblock()
889 self._visit(clause.body)
890 if self.flow.block:
891 self.flow.block.add_child(next_block)
892 # Else clause
893 if node.else_clause:
894 self.flow.nextblock(parent=parent)
895 self._visit(node.else_clause)
896 if self.flow.block:
897 self.flow.block.add_child(next_block)
898 else:
899 parent.add_child(next_block)
901 if next_block.parents:
902 self.flow.block = next_block
903 else:
904 self.flow.block = None
905 return node
907 def visit_WhileStatNode(self, node):
908 condition_block = self.flow.nextblock()
909 next_block = self.flow.newblock()
910 # Condition block
911 self.flow.loops.append(LoopDescr(next_block, condition_block))
912 if node.condition:
913 self._visit(node.condition)
914 # Body block
915 self.flow.nextblock()
916 self._visit(node.body)
917 self.flow.loops.pop()
918 # Loop it
919 if self.flow.block:
920 self.flow.block.add_child(condition_block)
921 self.flow.block.add_child(next_block)
922 # Else clause
923 if node.else_clause:
924 self.flow.nextblock(parent=condition_block)
925 self._visit(node.else_clause)
926 if self.flow.block:
927 self.flow.block.add_child(next_block)
928 else:
929 condition_block.add_child(next_block)
931 if next_block.parents:
932 self.flow.block = next_block
933 else:
934 self.flow.block = None
935 return node
937 def mark_forloop_target(self, node):
938 # TODO: Remove redundancy with range optimization...
939 is_special = False
940 sequence = node.iterator.sequence
941 target = node.target
942 if isinstance(sequence, ExprNodes.SimpleCallNode):
943 function = sequence.function
944 if sequence.self is None and function.is_name:
945 entry = self.env.lookup(function.name)
946 if not entry or entry.is_builtin:
947 if function.name == 'reversed' and len(sequence.args) == 1:
948 sequence = sequence.args[0]
949 elif function.name == 'enumerate' and len(sequence.args) == 1:
950 if target.is_sequence_constructor and len(target.args) == 2:
951 iterator = sequence.args[0]
952 if iterator.is_name:
953 iterator_type = iterator.infer_type(self.env)
954 if iterator_type.is_builtin_type:
955 # assume that builtin types have a length within Py_ssize_t
956 self.mark_assignment(
957 target.args[0],
958 ExprNodes.IntNode(target.pos, value='PY_SSIZE_T_MAX',
959 type=PyrexTypes.c_py_ssize_t_type))
960 target = target.args[1]
961 sequence = sequence.args[0]
962 if isinstance(sequence, ExprNodes.SimpleCallNode):
963 function = sequence.function
964 if sequence.self is None and function.is_name:
965 entry = self.env.lookup(function.name)
966 if not entry or entry.is_builtin:
967 if function.name in ('range', 'xrange'):
968 is_special = True
969 for arg in sequence.args[:2]:
970 self.mark_assignment(target, arg)
971 if len(sequence.args) > 2:
972 self.mark_assignment(
973 target,
974 ExprNodes.binop_node(node.pos,
975 '+',
976 sequence.args[0],
977 sequence.args[2]))
979 if not is_special:
980 # A for-loop basically translates to subsequent calls to
981 # __getitem__(), so using an IndexNode here allows us to
982 # naturally infer the base type of pointers, C arrays,
983 # Python strings, etc., while correctly falling back to an
984 # object type when the base type cannot be handled.
986 self.mark_assignment(target, node.item)
988 def visit_ForInStatNode(self, node):
989 condition_block = self.flow.nextblock()
990 next_block = self.flow.newblock()
991 # Condition with iterator
992 self.flow.loops.append(LoopDescr(next_block, condition_block))
993 self._visit(node.iterator)
994 # Target assignment
995 self.flow.nextblock()
997 if isinstance(node, Nodes.ForInStatNode):
998 self.mark_forloop_target(node)
999 else: # Parallel
1000 self.mark_assignment(node.target)
1002 # Body block
1003 if isinstance(node, Nodes.ParallelRangeNode):
1004 # In case of an invalid
1005 self._delete_privates(node, exclude=node.target.entry)
1007 self.flow.nextblock()
1008 self._visit(node.body)
1009 self.flow.loops.pop()
1011 # Loop it
1012 if self.flow.block:
1013 self.flow.block.add_child(condition_block)
1014 # Else clause
1015 if node.else_clause:
1016 self.flow.nextblock(parent=condition_block)
1017 self._visit(node.else_clause)
1018 if self.flow.block:
1019 self.flow.block.add_child(next_block)
1020 else:
1021 condition_block.add_child(next_block)
1023 if next_block.parents:
1024 self.flow.block = next_block
1025 else:
1026 self.flow.block = None
1027 return node
1029 def _delete_privates(self, node, exclude=None):
1030 for private_node in node.assigned_nodes:
1031 if not exclude or private_node.entry is not exclude:
1032 self.flow.mark_deletion(private_node, private_node.entry)
1034 def visit_ParallelRangeNode(self, node):
1035 reductions = self.reductions
1037 # if node.target is None or not a NameNode, an error will have
1038 # been previously issued
1039 if hasattr(node.target, 'entry'):
1040 self.reductions = set(reductions)
1042 for private_node in node.assigned_nodes:
1043 private_node.entry.error_on_uninitialized = True
1044 pos, reduction = node.assignments[private_node.entry]
1045 if reduction:
1046 self.reductions.add(private_node.entry)
1048 node = self.visit_ForInStatNode(node)
1050 self.reductions = reductions
1051 return node
1053 def visit_ParallelWithBlockNode(self, node):
1054 for private_node in node.assigned_nodes:
1055 private_node.entry.error_on_uninitialized = True
1057 self._delete_privates(node)
1058 self.visitchildren(node)
1059 self._delete_privates(node)
1061 return node
1063 def visit_ForFromStatNode(self, node):
1064 condition_block = self.flow.nextblock()
1065 next_block = self.flow.newblock()
1066 # Condition with iterator
1067 self.flow.loops.append(LoopDescr(next_block, condition_block))
1068 self._visit(node.bound1)
1069 self._visit(node.bound2)
1070 if node.step is not None:
1071 self._visit(node.step)
1072 # Target assignment
1073 self.flow.nextblock()
1074 self.mark_assignment(node.target, node.bound1)
1075 if node.step is not None:
1076 self.mark_assignment(node.target,
1077 ExprNodes.binop_node(node.pos, '+',
1078 node.bound1, node.step))
1079 # Body block
1080 self.flow.nextblock()
1081 self._visit(node.body)
1082 self.flow.loops.pop()
1083 # Loop it
1084 if self.flow.block:
1085 self.flow.block.add_child(condition_block)
1086 # Else clause
1087 if node.else_clause:
1088 self.flow.nextblock(parent=condition_block)
1089 self._visit(node.else_clause)
1090 if self.flow.block:
1091 self.flow.block.add_child(next_block)
1092 else:
1093 condition_block.add_child(next_block)
1095 if next_block.parents:
1096 self.flow.block = next_block
1097 else:
1098 self.flow.block = None
1099 return node
1101 def visit_LoopNode(self, node):
1102 raise InternalError("Generic loops are not supported")
1104 def visit_WithTargetAssignmentStatNode(self, node):
1105 self.mark_assignment(node.lhs, node.rhs)
1106 return node
1108 def visit_WithStatNode(self, node):
1109 self._visit(node.manager)
1110 self._visit(node.enter_call)
1111 self._visit(node.body)
1112 return node
1114 def visit_TryExceptStatNode(self, node):
1115 # After exception handling
1116 next_block = self.flow.newblock()
1117 # Body block
1118 self.flow.newblock()
1119 # Exception entry point
1120 entry_point = self.flow.newblock()
1121 self.flow.exceptions.append(ExceptionDescr(entry_point))
1122 self.flow.nextblock()
1123 ## XXX: links to exception handling point should be added by
1124 ## XXX: children nodes
1125 self.flow.block.add_child(entry_point)
1126 self.flow.nextblock()
1127 self._visit(node.body)
1128 self.flow.exceptions.pop()
1130 # After exception
1131 if self.flow.block:
1132 if node.else_clause:
1133 self.flow.nextblock()
1134 self._visit(node.else_clause)
1135 if self.flow.block:
1136 self.flow.block.add_child(next_block)
1138 for clause in node.except_clauses:
1139 self.flow.block = entry_point
1140 if clause.pattern:
1141 for pattern in clause.pattern:
1142 self._visit(pattern)
1143 else:
1144 # TODO: handle * pattern
1145 pass
1146 entry_point = self.flow.newblock(parent=self.flow.block)
1147 self.flow.nextblock()
1148 if clause.target:
1149 self.mark_assignment(clause.target)
1150 self._visit(clause.body)
1151 if self.flow.block:
1152 self.flow.block.add_child(next_block)
1154 if self.flow.exceptions:
1155 entry_point.add_child(self.flow.exceptions[-1].entry_point)
1157 if next_block.parents:
1158 self.flow.block = next_block
1159 else:
1160 self.flow.block = None
1161 return node
1163 def visit_TryFinallyStatNode(self, node):
1164 body_block = self.flow.nextblock()
1166 # Exception entry point
1167 entry_point = self.flow.newblock()
1168 self.flow.block = entry_point
1169 self._visit(node.finally_clause)
1171 if self.flow.block and self.flow.exceptions:
1172 self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1174 # Normal execution
1175 finally_enter = self.flow.newblock()
1176 self.flow.block = finally_enter
1177 self._visit(node.finally_clause)
1178 finally_exit = self.flow.block
1180 descr = ExceptionDescr(entry_point, finally_enter, finally_exit)
1181 self.flow.exceptions.append(descr)
1182 if self.flow.loops:
1183 self.flow.loops[-1].exceptions.append(descr)
1184 self.flow.block = body_block
1185 ## XXX: Is it still required
1186 body_block.add_child(entry_point)
1187 self.flow.nextblock()
1188 self._visit(node.body)
1189 self.flow.exceptions.pop()
1190 if self.flow.loops:
1191 self.flow.loops[-1].exceptions.pop()
1193 if self.flow.block:
1194 self.flow.block.add_child(finally_enter)
1195 if finally_exit:
1196 self.flow.block = self.flow.nextblock(parent=finally_exit)
1197 else:
1198 self.flow.block = None
1199 return node
1201 def visit_RaiseStatNode(self, node):
1202 self.mark_position(node)
1203 self.visitchildren(node)
1204 if self.flow.exceptions:
1205 self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1206 self.flow.block = None
1207 return node
1209 def visit_ReraiseStatNode(self, node):
1210 self.mark_position(node)
1211 if self.flow.exceptions:
1212 self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1213 self.flow.block = None
1214 return node
1216 def visit_ReturnStatNode(self, node):
1217 self.mark_position(node)
1218 self.visitchildren(node)
1220 for exception in self.flow.exceptions[::-1]:
1221 if exception.finally_enter:
1222 self.flow.block.add_child(exception.finally_enter)
1223 if exception.finally_exit:
1224 exception.finally_exit.add_child(self.flow.exit_point)
1225 break
1226 else:
1227 if self.flow.block:
1228 self.flow.block.add_child(self.flow.exit_point)
1229 self.flow.block = None
1230 return node
1232 def visit_BreakStatNode(self, node):
1233 if not self.flow.loops:
1234 #error(node.pos, "break statement not inside loop")
1235 return node
1236 loop = self.flow.loops[-1]
1237 self.mark_position(node)
1238 for exception in loop.exceptions[::-1]:
1239 if exception.finally_enter:
1240 self.flow.block.add_child(exception.finally_enter)
1241 if exception.finally_exit:
1242 exception.finally_exit.add_child(loop.next_block)
1243 break
1244 else:
1245 self.flow.block.add_child(loop.next_block)
1246 self.flow.block = None
1247 return node
1249 def visit_ContinueStatNode(self, node):
1250 if not self.flow.loops:
1251 #error(node.pos, "continue statement not inside loop")
1252 return node
1253 loop = self.flow.loops[-1]
1254 self.mark_position(node)
1255 for exception in loop.exceptions[::-1]:
1256 if exception.finally_enter:
1257 self.flow.block.add_child(exception.finally_enter)
1258 if exception.finally_exit:
1259 exception.finally_exit.add_child(loop.loop_block)
1260 break
1261 else:
1262 self.flow.block.add_child(loop.loop_block)
1263 self.flow.block = None
1264 return node
1266 def visit_ComprehensionNode(self, node):
1267 if node.expr_scope:
1268 self.env_stack.append(self.env)
1269 self.env = node.expr_scope
1270 # Skip append node here
1271 self._visit(node.loop)
1272 if node.expr_scope:
1273 self.env = self.env_stack.pop()
1274 return node
1276 def visit_ScopedExprNode(self, node):
1277 if node.expr_scope:
1278 self.env_stack.append(self.env)
1279 self.env = node.expr_scope
1280 self.visitchildren(node)
1281 if node.expr_scope:
1282 self.env = self.env_stack.pop()
1283 return node
1285 def visit_PyClassDefNode(self, node):
1286 self.visitchildren(node, ('dict', 'metaclass',
1287 'mkw', 'bases', 'class_result'))
1288 self.flow.mark_assignment(node.target, object_expr_not_none,
1289 self.env.lookup(node.name))
1290 self.env_stack.append(self.env)
1291 self.env = node.scope
1292 self.flow.nextblock()
1293 self.visitchildren(node, ('body',))
1294 self.flow.nextblock()
1295 self.env = self.env_stack.pop()
1296 return node
1298 def visit_AmpersandNode(self, node):
1299 if node.operand.is_name:
1300 # Fake assignment to silence warning
1301 self.mark_assignment(node.operand, fake_rhs_expr)
1302 self.visitchildren(node)
1303 return node