FlowControl.py 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325
  1. from __future__ import absolute_import
  2. import cython
  3. cython.declare(PyrexTypes=object, ExprNodes=object, Nodes=object,
  4. Builtin=object, InternalError=object, error=object, warning=object,
  5. py_object_type=object, unspecified_type=object,
  6. object_expr=object, fake_rhs_expr=object, TypedExprNode=object)
  7. from . import Builtin
  8. from . import ExprNodes
  9. from . import Nodes
  10. from . import Options
  11. from .PyrexTypes import py_object_type, unspecified_type
  12. from . import PyrexTypes
  13. from .Visitor import TreeVisitor, CythonTransform
  14. from .Errors import error, warning, InternalError
  15. from .Optimize import ConstantFolding
  16. class TypedExprNode(ExprNodes.ExprNode):
  17. # Used for declaring assignments of a specified type without a known entry.
  18. def __init__(self, type, may_be_none=None, pos=None):
  19. super(TypedExprNode, self).__init__(pos)
  20. self.type = type
  21. self._may_be_none = may_be_none
  22. def may_be_none(self):
  23. return self._may_be_none != False
  24. object_expr = TypedExprNode(py_object_type, may_be_none=True)
  25. # Fake rhs to silence "unused variable" warning
  26. fake_rhs_expr = TypedExprNode(unspecified_type)
  27. class ControlBlock(object):
  28. """Control flow graph node. Sequence of assignments and name references.
  29. children set of children nodes
  30. parents set of parent nodes
  31. positions set of position markers
  32. stats list of block statements
  33. gen dict of assignments generated by this block
  34. bounded set of entries that are definitely bounded in this block
  35. Example:
  36. a = 1
  37. b = a + c # 'c' is already bounded or exception here
  38. stats = [Assignment(a), NameReference(a), NameReference(c),
  39. Assignment(b)]
  40. gen = {Entry(a): Assignment(a), Entry(b): Assignment(b)}
  41. bounded = set([Entry(a), Entry(c)])
  42. """
  43. def __init__(self):
  44. self.children = set()
  45. self.parents = set()
  46. self.positions = set()
  47. self.stats = []
  48. self.gen = {}
  49. self.bounded = set()
  50. self.i_input = 0
  51. self.i_output = 0
  52. self.i_gen = 0
  53. self.i_kill = 0
  54. self.i_state = 0
  55. def empty(self):
  56. return (not self.stats and not self.positions)
  57. def detach(self):
  58. """Detach block from parents and children."""
  59. for child in self.children:
  60. child.parents.remove(self)
  61. for parent in self.parents:
  62. parent.children.remove(self)
  63. self.parents.clear()
  64. self.children.clear()
  65. def add_child(self, block):
  66. self.children.add(block)
  67. block.parents.add(self)
  68. class ExitBlock(ControlBlock):
  69. """Non-empty exit point block."""
  70. def empty(self):
  71. return False
  72. class AssignmentList(object):
  73. def __init__(self):
  74. self.stats = []
  75. class ControlFlow(object):
  76. """Control-flow graph.
  77. entry_point ControlBlock entry point for this graph
  78. exit_point ControlBlock normal exit point
  79. block ControlBlock current block
  80. blocks set children nodes
  81. entries set tracked entries
  82. loops list stack for loop descriptors
  83. exceptions list stack for exception descriptors
  84. """
  85. def __init__(self):
  86. self.blocks = set()
  87. self.entries = set()
  88. self.loops = []
  89. self.exceptions = []
  90. self.entry_point = ControlBlock()
  91. self.exit_point = ExitBlock()
  92. self.blocks.add(self.exit_point)
  93. self.block = self.entry_point
  94. def newblock(self, parent=None):
  95. """Create floating block linked to `parent` if given.
  96. NOTE: Block is NOT added to self.blocks
  97. """
  98. block = ControlBlock()
  99. self.blocks.add(block)
  100. if parent:
  101. parent.add_child(block)
  102. return block
  103. def nextblock(self, parent=None):
  104. """Create block children block linked to current or `parent` if given.
  105. NOTE: Block is added to self.blocks
  106. """
  107. block = ControlBlock()
  108. self.blocks.add(block)
  109. if parent:
  110. parent.add_child(block)
  111. elif self.block:
  112. self.block.add_child(block)
  113. self.block = block
  114. return self.block
  115. def is_tracked(self, entry):
  116. if entry.is_anonymous:
  117. return False
  118. return (entry.is_local or entry.is_pyclass_attr or entry.is_arg or
  119. entry.from_closure or entry.in_closure or
  120. entry.error_on_uninitialized)
  121. def is_statically_assigned(self, entry):
  122. if (entry.is_local and entry.is_variable and
  123. (entry.type.is_struct_or_union or
  124. entry.type.is_complex or
  125. entry.type.is_array or
  126. entry.type.is_cpp_class)):
  127. # stack allocated structured variable => never uninitialised
  128. return True
  129. return False
  130. def mark_position(self, node):
  131. """Mark position, will be used to draw graph nodes."""
  132. if self.block:
  133. self.block.positions.add(node.pos[:2])
  134. def mark_assignment(self, lhs, rhs, entry):
  135. if self.block and self.is_tracked(entry):
  136. assignment = NameAssignment(lhs, rhs, entry)
  137. self.block.stats.append(assignment)
  138. self.block.gen[entry] = assignment
  139. self.entries.add(entry)
  140. def mark_argument(self, lhs, rhs, entry):
  141. if self.block and self.is_tracked(entry):
  142. assignment = Argument(lhs, rhs, entry)
  143. self.block.stats.append(assignment)
  144. self.block.gen[entry] = assignment
  145. self.entries.add(entry)
  146. def mark_deletion(self, node, entry):
  147. if self.block and self.is_tracked(entry):
  148. assignment = NameDeletion(node, entry)
  149. self.block.stats.append(assignment)
  150. self.block.gen[entry] = Uninitialized
  151. self.entries.add(entry)
  152. def mark_reference(self, node, entry):
  153. if self.block and self.is_tracked(entry):
  154. self.block.stats.append(NameReference(node, entry))
  155. ## XXX: We don't track expression evaluation order so we can't use
  156. ## XXX: successful reference as initialization sign.
  157. ## # Local variable is definitely bound after this reference
  158. ## if not node.allow_null:
  159. ## self.block.bounded.add(entry)
  160. self.entries.add(entry)
  161. def normalize(self):
  162. """Delete unreachable and orphan blocks."""
  163. queue = set([self.entry_point])
  164. visited = set()
  165. while queue:
  166. root = queue.pop()
  167. visited.add(root)
  168. for child in root.children:
  169. if child not in visited:
  170. queue.add(child)
  171. unreachable = self.blocks - visited
  172. for block in unreachable:
  173. block.detach()
  174. visited.remove(self.entry_point)
  175. for block in visited:
  176. if block.empty():
  177. for parent in block.parents: # Re-parent
  178. for child in block.children:
  179. parent.add_child(child)
  180. block.detach()
  181. unreachable.add(block)
  182. self.blocks -= unreachable
  183. def initialize(self):
  184. """Set initial state, map assignments to bits."""
  185. self.assmts = {}
  186. bit = 1
  187. for entry in self.entries:
  188. assmts = AssignmentList()
  189. assmts.mask = assmts.bit = bit
  190. self.assmts[entry] = assmts
  191. bit <<= 1
  192. for block in self.blocks:
  193. for stat in block.stats:
  194. if isinstance(stat, NameAssignment):
  195. stat.bit = bit
  196. assmts = self.assmts[stat.entry]
  197. assmts.stats.append(stat)
  198. assmts.mask |= bit
  199. bit <<= 1
  200. for block in self.blocks:
  201. for entry, stat in block.gen.items():
  202. assmts = self.assmts[entry]
  203. if stat is Uninitialized:
  204. block.i_gen |= assmts.bit
  205. else:
  206. block.i_gen |= stat.bit
  207. block.i_kill |= assmts.mask
  208. block.i_output = block.i_gen
  209. for entry in block.bounded:
  210. block.i_kill |= self.assmts[entry].bit
  211. for assmts in self.assmts.values():
  212. self.entry_point.i_gen |= assmts.bit
  213. self.entry_point.i_output = self.entry_point.i_gen
  214. def map_one(self, istate, entry):
  215. ret = set()
  216. assmts = self.assmts[entry]
  217. if istate & assmts.bit:
  218. if self.is_statically_assigned(entry):
  219. ret.add(StaticAssignment(entry))
  220. elif entry.from_closure:
  221. ret.add(Unknown)
  222. else:
  223. ret.add(Uninitialized)
  224. for assmt in assmts.stats:
  225. if istate & assmt.bit:
  226. ret.add(assmt)
  227. return ret
  228. def reaching_definitions(self):
  229. """Per-block reaching definitions analysis."""
  230. dirty = True
  231. while dirty:
  232. dirty = False
  233. for block in self.blocks:
  234. i_input = 0
  235. for parent in block.parents:
  236. i_input |= parent.i_output
  237. i_output = (i_input & ~block.i_kill) | block.i_gen
  238. if i_output != block.i_output:
  239. dirty = True
  240. block.i_input = i_input
  241. block.i_output = i_output
  242. class LoopDescr(object):
  243. def __init__(self, next_block, loop_block):
  244. self.next_block = next_block
  245. self.loop_block = loop_block
  246. self.exceptions = []
  247. class ExceptionDescr(object):
  248. """Exception handling helper.
  249. entry_point ControlBlock Exception handling entry point
  250. finally_enter ControlBlock Normal finally clause entry point
  251. finally_exit ControlBlock Normal finally clause exit point
  252. """
  253. def __init__(self, entry_point, finally_enter=None, finally_exit=None):
  254. self.entry_point = entry_point
  255. self.finally_enter = finally_enter
  256. self.finally_exit = finally_exit
  257. class NameAssignment(object):
  258. def __init__(self, lhs, rhs, entry):
  259. if lhs.cf_state is None:
  260. lhs.cf_state = set()
  261. self.lhs = lhs
  262. self.rhs = rhs
  263. self.entry = entry
  264. self.pos = lhs.pos
  265. self.refs = set()
  266. self.is_arg = False
  267. self.is_deletion = False
  268. self.inferred_type = None
  269. def __repr__(self):
  270. return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
  271. def infer_type(self):
  272. self.inferred_type = self.rhs.infer_type(self.entry.scope)
  273. return self.inferred_type
  274. def type_dependencies(self):
  275. return self.rhs.type_dependencies(self.entry.scope)
  276. @property
  277. def type(self):
  278. if not self.entry.type.is_unspecified:
  279. return self.entry.type
  280. return self.inferred_type
  281. class StaticAssignment(NameAssignment):
  282. """Initialised at declaration time, e.g. stack allocation."""
  283. def __init__(self, entry):
  284. if not entry.type.is_pyobject:
  285. may_be_none = False
  286. else:
  287. may_be_none = None # unknown
  288. lhs = TypedExprNode(
  289. entry.type, may_be_none=may_be_none, pos=entry.pos)
  290. super(StaticAssignment, self).__init__(lhs, lhs, entry)
  291. def infer_type(self):
  292. return self.entry.type
  293. def type_dependencies(self):
  294. return ()
  295. class Argument(NameAssignment):
  296. def __init__(self, lhs, rhs, entry):
  297. NameAssignment.__init__(self, lhs, rhs, entry)
  298. self.is_arg = True
  299. class NameDeletion(NameAssignment):
  300. def __init__(self, lhs, entry):
  301. NameAssignment.__init__(self, lhs, lhs, entry)
  302. self.is_deletion = True
  303. def infer_type(self):
  304. inferred_type = self.rhs.infer_type(self.entry.scope)
  305. if (not inferred_type.is_pyobject and
  306. inferred_type.can_coerce_to_pyobject(self.entry.scope)):
  307. return py_object_type
  308. self.inferred_type = inferred_type
  309. return inferred_type
  310. class Uninitialized(object):
  311. """Definitely not initialised yet."""
  312. class Unknown(object):
  313. """Coming from outer closure, might be initialised or not."""
  314. class NameReference(object):
  315. def __init__(self, node, entry):
  316. if node.cf_state is None:
  317. node.cf_state = set()
  318. self.node = node
  319. self.entry = entry
  320. self.pos = node.pos
  321. def __repr__(self):
  322. return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
  323. class ControlFlowState(list):
  324. # Keeps track of Node's entry assignments
  325. #
  326. # cf_is_null [boolean] It is uninitialized
  327. # cf_maybe_null [boolean] May be uninitialized
  328. # is_single [boolean] Has only one assignment at this point
  329. cf_maybe_null = False
  330. cf_is_null = False
  331. is_single = False
  332. def __init__(self, state):
  333. if Uninitialized in state:
  334. state.discard(Uninitialized)
  335. self.cf_maybe_null = True
  336. if not state:
  337. self.cf_is_null = True
  338. elif Unknown in state:
  339. state.discard(Unknown)
  340. self.cf_maybe_null = True
  341. else:
  342. if len(state) == 1:
  343. self.is_single = True
  344. # XXX: Remove fake_rhs_expr
  345. super(ControlFlowState, self).__init__(
  346. [i for i in state if i.rhs is not fake_rhs_expr])
  347. def one(self):
  348. return self[0]
  349. class GVContext(object):
  350. """Graphviz subgraph object."""
  351. def __init__(self):
  352. self.blockids = {}
  353. self.nextid = 0
  354. self.children = []
  355. self.sources = {}
  356. def add(self, child):
  357. self.children.append(child)
  358. def nodeid(self, block):
  359. if block not in self.blockids:
  360. self.blockids[block] = 'block%d' % self.nextid
  361. self.nextid += 1
  362. return self.blockids[block]
  363. def extract_sources(self, block):
  364. if not block.positions:
  365. return ''
  366. start = min(block.positions)
  367. stop = max(block.positions)
  368. srcdescr = start[0]
  369. if not srcdescr in self.sources:
  370. self.sources[srcdescr] = list(srcdescr.get_lines())
  371. lines = self.sources[srcdescr]
  372. return '\\n'.join([l.strip() for l in lines[start[1] - 1:stop[1]]])
  373. def render(self, fp, name, annotate_defs=False):
  374. """Render graphviz dot graph"""
  375. fp.write('digraph %s {\n' % name)
  376. fp.write(' node [shape=box];\n')
  377. for child in self.children:
  378. child.render(fp, self, annotate_defs)
  379. fp.write('}\n')
  380. def escape(self, text):
  381. return text.replace('"', '\\"').replace('\n', '\\n')
  382. class GV(object):
  383. """Graphviz DOT renderer."""
  384. def __init__(self, name, flow):
  385. self.name = name
  386. self.flow = flow
  387. def render(self, fp, ctx, annotate_defs=False):
  388. fp.write(' subgraph %s {\n' % self.name)
  389. for block in self.flow.blocks:
  390. label = ctx.extract_sources(block)
  391. if annotate_defs:
  392. for stat in block.stats:
  393. if isinstance(stat, NameAssignment):
  394. label += '\n %s [%s %s]' % (
  395. stat.entry.name, 'deletion' if stat.is_deletion else 'definition', stat.pos[1])
  396. elif isinstance(stat, NameReference):
  397. if stat.entry:
  398. label += '\n %s [reference %s]' % (stat.entry.name, stat.pos[1])
  399. if not label:
  400. label = 'empty'
  401. pid = ctx.nodeid(block)
  402. fp.write(' %s [label="%s"];\n' % (pid, ctx.escape(label)))
  403. for block in self.flow.blocks:
  404. pid = ctx.nodeid(block)
  405. for child in block.children:
  406. fp.write(' %s -> %s;\n' % (pid, ctx.nodeid(child)))
  407. fp.write(' }\n')
  408. class MessageCollection(object):
  409. """Collect error/warnings messages first then sort"""
  410. def __init__(self):
  411. self.messages = set()
  412. def error(self, pos, message):
  413. self.messages.add((pos, True, message))
  414. def warning(self, pos, message):
  415. self.messages.add((pos, False, message))
  416. def report(self):
  417. for pos, is_error, message in sorted(self.messages):
  418. if is_error:
  419. error(pos, message)
  420. else:
  421. warning(pos, message, 2)
  422. def check_definitions(flow, compiler_directives):
  423. flow.initialize()
  424. flow.reaching_definitions()
  425. # Track down state
  426. assignments = set()
  427. # Node to entry map
  428. references = {}
  429. assmt_nodes = set()
  430. for block in flow.blocks:
  431. i_state = block.i_input
  432. for stat in block.stats:
  433. i_assmts = flow.assmts[stat.entry]
  434. state = flow.map_one(i_state, stat.entry)
  435. if isinstance(stat, NameAssignment):
  436. stat.lhs.cf_state.update(state)
  437. assmt_nodes.add(stat.lhs)
  438. i_state = i_state & ~i_assmts.mask
  439. if stat.is_deletion:
  440. i_state |= i_assmts.bit
  441. else:
  442. i_state |= stat.bit
  443. assignments.add(stat)
  444. if stat.rhs is not fake_rhs_expr:
  445. stat.entry.cf_assignments.append(stat)
  446. elif isinstance(stat, NameReference):
  447. references[stat.node] = stat.entry
  448. stat.entry.cf_references.append(stat)
  449. stat.node.cf_state.update(state)
  450. ## if not stat.node.allow_null:
  451. ## i_state &= ~i_assmts.bit
  452. ## # after successful read, the state is known to be initialised
  453. state.discard(Uninitialized)
  454. state.discard(Unknown)
  455. for assmt in state:
  456. assmt.refs.add(stat)
  457. # Check variable usage
  458. warn_maybe_uninitialized = compiler_directives['warn.maybe_uninitialized']
  459. warn_unused_result = compiler_directives['warn.unused_result']
  460. warn_unused = compiler_directives['warn.unused']
  461. warn_unused_arg = compiler_directives['warn.unused_arg']
  462. messages = MessageCollection()
  463. # assignment hints
  464. for node in assmt_nodes:
  465. if Uninitialized in node.cf_state:
  466. node.cf_maybe_null = True
  467. if len(node.cf_state) == 1:
  468. node.cf_is_null = True
  469. else:
  470. node.cf_is_null = False
  471. elif Unknown in node.cf_state:
  472. node.cf_maybe_null = True
  473. else:
  474. node.cf_is_null = False
  475. node.cf_maybe_null = False
  476. # Find uninitialized references and cf-hints
  477. for node, entry in references.items():
  478. if Uninitialized in node.cf_state:
  479. node.cf_maybe_null = True
  480. if not entry.from_closure and len(node.cf_state) == 1:
  481. node.cf_is_null = True
  482. if (node.allow_null or entry.from_closure
  483. or entry.is_pyclass_attr or entry.type.is_error):
  484. pass # Can be uninitialized here
  485. elif node.cf_is_null:
  486. if entry.error_on_uninitialized or (
  487. Options.error_on_uninitialized and (
  488. entry.type.is_pyobject or entry.type.is_unspecified)):
  489. messages.error(
  490. node.pos,
  491. "local variable '%s' referenced before assignment"
  492. % entry.name)
  493. else:
  494. messages.warning(
  495. node.pos,
  496. "local variable '%s' referenced before assignment"
  497. % entry.name)
  498. elif warn_maybe_uninitialized:
  499. messages.warning(
  500. node.pos,
  501. "local variable '%s' might be referenced before assignment"
  502. % entry.name)
  503. elif Unknown in node.cf_state:
  504. # TODO: better cross-closure analysis to know when inner functions
  505. # are being called before a variable is being set, and when
  506. # a variable is known to be set before even defining the
  507. # inner function, etc.
  508. node.cf_maybe_null = True
  509. else:
  510. node.cf_is_null = False
  511. node.cf_maybe_null = False
  512. # Unused result
  513. for assmt in assignments:
  514. if (not assmt.refs and not assmt.entry.is_pyclass_attr
  515. and not assmt.entry.in_closure):
  516. if assmt.entry.cf_references and warn_unused_result:
  517. if assmt.is_arg:
  518. messages.warning(assmt.pos, "Unused argument value '%s'" %
  519. assmt.entry.name)
  520. else:
  521. messages.warning(assmt.pos, "Unused result in '%s'" %
  522. assmt.entry.name)
  523. assmt.lhs.cf_used = False
  524. # Unused entries
  525. for entry in flow.entries:
  526. if (not entry.cf_references
  527. and not entry.is_pyclass_attr):
  528. if entry.name != '_' and not entry.name.startswith('unused'):
  529. # '_' is often used for unused variables, e.g. in loops
  530. if entry.is_arg:
  531. if warn_unused_arg:
  532. messages.warning(entry.pos, "Unused argument '%s'" %
  533. entry.name)
  534. else:
  535. if warn_unused:
  536. messages.warning(entry.pos, "Unused entry '%s'" %
  537. entry.name)
  538. entry.cf_used = False
  539. messages.report()
  540. for node in assmt_nodes:
  541. node.cf_state = ControlFlowState(node.cf_state)
  542. for node in references:
  543. node.cf_state = ControlFlowState(node.cf_state)
  544. class AssignmentCollector(TreeVisitor):
  545. def __init__(self):
  546. super(AssignmentCollector, self).__init__()
  547. self.assignments = []
  548. def visit_Node(self):
  549. self._visitchildren(self, None)
  550. def visit_SingleAssignmentNode(self, node):
  551. self.assignments.append((node.lhs, node.rhs))
  552. def visit_CascadedAssignmentNode(self, node):
  553. for lhs in node.lhs_list:
  554. self.assignments.append((lhs, node.rhs))
  555. class ControlFlowAnalysis(CythonTransform):
  556. def visit_ModuleNode(self, node):
  557. self.gv_ctx = GVContext()
  558. self.constant_folder = ConstantFolding()
  559. # Set of NameNode reductions
  560. self.reductions = set()
  561. self.in_inplace_assignment = False
  562. self.env_stack = []
  563. self.env = node.scope
  564. self.stack = []
  565. self.flow = ControlFlow()
  566. self.visitchildren(node)
  567. check_definitions(self.flow, self.current_directives)
  568. dot_output = self.current_directives['control_flow.dot_output']
  569. if dot_output:
  570. annotate_defs = self.current_directives['control_flow.dot_annotate_defs']
  571. fp = open(dot_output, 'wt')
  572. try:
  573. self.gv_ctx.render(fp, 'module', annotate_defs=annotate_defs)
  574. finally:
  575. fp.close()
  576. return node
  577. def visit_FuncDefNode(self, node):
  578. for arg in node.args:
  579. if arg.default:
  580. self.visitchildren(arg)
  581. self.visitchildren(node, ('decorators',))
  582. self.env_stack.append(self.env)
  583. self.env = node.local_scope
  584. self.stack.append(self.flow)
  585. self.flow = ControlFlow()
  586. # Collect all entries
  587. for entry in node.local_scope.entries.values():
  588. if self.flow.is_tracked(entry):
  589. self.flow.entries.add(entry)
  590. self.mark_position(node)
  591. # Function body block
  592. self.flow.nextblock()
  593. for arg in node.args:
  594. self._visit(arg)
  595. if node.star_arg:
  596. self.flow.mark_argument(node.star_arg,
  597. TypedExprNode(Builtin.tuple_type,
  598. may_be_none=False),
  599. node.star_arg.entry)
  600. if node.starstar_arg:
  601. self.flow.mark_argument(node.starstar_arg,
  602. TypedExprNode(Builtin.dict_type,
  603. may_be_none=False),
  604. node.starstar_arg.entry)
  605. self._visit(node.body)
  606. # Workaround for generators
  607. if node.is_generator:
  608. self._visit(node.gbody.body)
  609. # Exit point
  610. if self.flow.block:
  611. self.flow.block.add_child(self.flow.exit_point)
  612. # Cleanup graph
  613. self.flow.normalize()
  614. check_definitions(self.flow, self.current_directives)
  615. self.flow.blocks.add(self.flow.entry_point)
  616. self.gv_ctx.add(GV(node.local_scope.name, self.flow))
  617. self.flow = self.stack.pop()
  618. self.env = self.env_stack.pop()
  619. return node
  620. def visit_DefNode(self, node):
  621. node.used = True
  622. return self.visit_FuncDefNode(node)
  623. def visit_GeneratorBodyDefNode(self, node):
  624. return node
  625. def visit_CTypeDefNode(self, node):
  626. return node
  627. def mark_assignment(self, lhs, rhs=None):
  628. if not self.flow.block:
  629. return
  630. if self.flow.exceptions:
  631. exc_descr = self.flow.exceptions[-1]
  632. self.flow.block.add_child(exc_descr.entry_point)
  633. self.flow.nextblock()
  634. if not rhs:
  635. rhs = object_expr
  636. if lhs.is_name:
  637. if lhs.entry is not None:
  638. entry = lhs.entry
  639. else:
  640. entry = self.env.lookup(lhs.name)
  641. if entry is None: # TODO: This shouldn't happen...
  642. return
  643. self.flow.mark_assignment(lhs, rhs, entry)
  644. elif lhs.is_sequence_constructor:
  645. for i, arg in enumerate(lhs.args):
  646. if not rhs or arg.is_starred:
  647. item_node = None
  648. else:
  649. item_node = rhs.inferable_item_node(i)
  650. self.mark_assignment(arg, item_node)
  651. else:
  652. self._visit(lhs)
  653. if self.flow.exceptions:
  654. exc_descr = self.flow.exceptions[-1]
  655. self.flow.block.add_child(exc_descr.entry_point)
  656. self.flow.nextblock()
  657. def mark_position(self, node):
  658. """Mark position if DOT output is enabled."""
  659. if self.current_directives['control_flow.dot_output']:
  660. self.flow.mark_position(node)
  661. def visit_FromImportStatNode(self, node):
  662. for name, target in node.items:
  663. if name != "*":
  664. self.mark_assignment(target)
  665. self.visitchildren(node)
  666. return node
  667. def visit_AssignmentNode(self, node):
  668. raise InternalError("Unhandled assignment node")
  669. def visit_SingleAssignmentNode(self, node):
  670. self._visit(node.rhs)
  671. self.mark_assignment(node.lhs, node.rhs)
  672. return node
  673. def visit_CascadedAssignmentNode(self, node):
  674. self._visit(node.rhs)
  675. for lhs in node.lhs_list:
  676. self.mark_assignment(lhs, node.rhs)
  677. return node
  678. def visit_ParallelAssignmentNode(self, node):
  679. collector = AssignmentCollector()
  680. collector.visitchildren(node)
  681. for lhs, rhs in collector.assignments:
  682. self._visit(rhs)
  683. for lhs, rhs in collector.assignments:
  684. self.mark_assignment(lhs, rhs)
  685. return node
  686. def visit_InPlaceAssignmentNode(self, node):
  687. self.in_inplace_assignment = True
  688. self.visitchildren(node)
  689. self.in_inplace_assignment = False
  690. self.mark_assignment(node.lhs, self.constant_folder(node.create_binop_node()))
  691. return node
  692. def visit_DelStatNode(self, node):
  693. for arg in node.args:
  694. if arg.is_name:
  695. entry = arg.entry or self.env.lookup(arg.name)
  696. if entry.in_closure or entry.from_closure:
  697. error(arg.pos,
  698. "can not delete variable '%s' "
  699. "referenced in nested scope" % entry.name)
  700. if not node.ignore_nonexisting:
  701. self._visit(arg) # mark reference
  702. self.flow.mark_deletion(arg, entry)
  703. else:
  704. self._visit(arg)
  705. return node
  706. def visit_CArgDeclNode(self, node):
  707. entry = self.env.lookup(node.name)
  708. if entry:
  709. may_be_none = not node.not_none
  710. self.flow.mark_argument(
  711. node, TypedExprNode(entry.type, may_be_none), entry)
  712. return node
  713. def visit_NameNode(self, node):
  714. if self.flow.block:
  715. entry = node.entry or self.env.lookup(node.name)
  716. if entry:
  717. self.flow.mark_reference(node, entry)
  718. if entry in self.reductions and not self.in_inplace_assignment:
  719. error(node.pos,
  720. "Cannot read reduction variable in loop body")
  721. return node
  722. def visit_StatListNode(self, node):
  723. if self.flow.block:
  724. for stat in node.stats:
  725. self._visit(stat)
  726. if not self.flow.block:
  727. stat.is_terminator = True
  728. break
  729. return node
  730. def visit_Node(self, node):
  731. self.visitchildren(node)
  732. self.mark_position(node)
  733. return node
  734. def visit_SizeofVarNode(self, node):
  735. return node
  736. def visit_TypeidNode(self, node):
  737. return node
  738. def visit_IfStatNode(self, node):
  739. next_block = self.flow.newblock()
  740. parent = self.flow.block
  741. # If clauses
  742. for clause in node.if_clauses:
  743. parent = self.flow.nextblock(parent)
  744. self._visit(clause.condition)
  745. self.flow.nextblock()
  746. self._visit(clause.body)
  747. if self.flow.block:
  748. self.flow.block.add_child(next_block)
  749. # Else clause
  750. if node.else_clause:
  751. self.flow.nextblock(parent=parent)
  752. self._visit(node.else_clause)
  753. if self.flow.block:
  754. self.flow.block.add_child(next_block)
  755. else:
  756. parent.add_child(next_block)
  757. if next_block.parents:
  758. self.flow.block = next_block
  759. else:
  760. self.flow.block = None
  761. return node
  762. def visit_WhileStatNode(self, node):
  763. condition_block = self.flow.nextblock()
  764. next_block = self.flow.newblock()
  765. # Condition block
  766. self.flow.loops.append(LoopDescr(next_block, condition_block))
  767. if node.condition:
  768. self._visit(node.condition)
  769. # Body block
  770. self.flow.nextblock()
  771. self._visit(node.body)
  772. self.flow.loops.pop()
  773. # Loop it
  774. if self.flow.block:
  775. self.flow.block.add_child(condition_block)
  776. self.flow.block.add_child(next_block)
  777. # Else clause
  778. if node.else_clause:
  779. self.flow.nextblock(parent=condition_block)
  780. self._visit(node.else_clause)
  781. if self.flow.block:
  782. self.flow.block.add_child(next_block)
  783. else:
  784. condition_block.add_child(next_block)
  785. if next_block.parents:
  786. self.flow.block = next_block
  787. else:
  788. self.flow.block = None
  789. return node
  790. def mark_forloop_target(self, node):
  791. # TODO: Remove redundancy with range optimization...
  792. is_special = False
  793. sequence = node.iterator.sequence
  794. target = node.target
  795. if isinstance(sequence, ExprNodes.SimpleCallNode):
  796. function = sequence.function
  797. if sequence.self is None and function.is_name:
  798. entry = self.env.lookup(function.name)
  799. if not entry or entry.is_builtin:
  800. if function.name == 'reversed' and len(sequence.args) == 1:
  801. sequence = sequence.args[0]
  802. elif function.name == 'enumerate' and len(sequence.args) == 1:
  803. if target.is_sequence_constructor and len(target.args) == 2:
  804. iterator = sequence.args[0]
  805. if iterator.is_name:
  806. iterator_type = iterator.infer_type(self.env)
  807. if iterator_type.is_builtin_type:
  808. # assume that builtin types have a length within Py_ssize_t
  809. self.mark_assignment(
  810. target.args[0],
  811. ExprNodes.IntNode(target.pos, value='PY_SSIZE_T_MAX',
  812. type=PyrexTypes.c_py_ssize_t_type))
  813. target = target.args[1]
  814. sequence = sequence.args[0]
  815. if isinstance(sequence, ExprNodes.SimpleCallNode):
  816. function = sequence.function
  817. if sequence.self is None and function.is_name:
  818. entry = self.env.lookup(function.name)
  819. if not entry or entry.is_builtin:
  820. if function.name in ('range', 'xrange'):
  821. is_special = True
  822. for arg in sequence.args[:2]:
  823. self.mark_assignment(target, arg)
  824. if len(sequence.args) > 2:
  825. self.mark_assignment(target, self.constant_folder(
  826. ExprNodes.binop_node(node.pos,
  827. '+',
  828. sequence.args[0],
  829. sequence.args[2])))
  830. if not is_special:
  831. # A for-loop basically translates to subsequent calls to
  832. # __getitem__(), so using an IndexNode here allows us to
  833. # naturally infer the base type of pointers, C arrays,
  834. # Python strings, etc., while correctly falling back to an
  835. # object type when the base type cannot be handled.
  836. self.mark_assignment(target, node.item)
  837. def visit_AsyncForStatNode(self, node):
  838. return self.visit_ForInStatNode(node)
  839. def visit_ForInStatNode(self, node):
  840. condition_block = self.flow.nextblock()
  841. next_block = self.flow.newblock()
  842. # Condition with iterator
  843. self.flow.loops.append(LoopDescr(next_block, condition_block))
  844. self._visit(node.iterator)
  845. # Target assignment
  846. self.flow.nextblock()
  847. if isinstance(node, Nodes.ForInStatNode):
  848. self.mark_forloop_target(node)
  849. elif isinstance(node, Nodes.AsyncForStatNode):
  850. # not entirely correct, but good enough for now
  851. self.mark_assignment(node.target, node.item)
  852. else: # Parallel
  853. self.mark_assignment(node.target)
  854. # Body block
  855. if isinstance(node, Nodes.ParallelRangeNode):
  856. # In case of an invalid
  857. self._delete_privates(node, exclude=node.target.entry)
  858. self.flow.nextblock()
  859. self._visit(node.body)
  860. self.flow.loops.pop()
  861. # Loop it
  862. if self.flow.block:
  863. self.flow.block.add_child(condition_block)
  864. # Else clause
  865. if node.else_clause:
  866. self.flow.nextblock(parent=condition_block)
  867. self._visit(node.else_clause)
  868. if self.flow.block:
  869. self.flow.block.add_child(next_block)
  870. else:
  871. condition_block.add_child(next_block)
  872. if next_block.parents:
  873. self.flow.block = next_block
  874. else:
  875. self.flow.block = None
  876. return node
  877. def _delete_privates(self, node, exclude=None):
  878. for private_node in node.assigned_nodes:
  879. if not exclude or private_node.entry is not exclude:
  880. self.flow.mark_deletion(private_node, private_node.entry)
  881. def visit_ParallelRangeNode(self, node):
  882. reductions = self.reductions
  883. # if node.target is None or not a NameNode, an error will have
  884. # been previously issued
  885. if hasattr(node.target, 'entry'):
  886. self.reductions = set(reductions)
  887. for private_node in node.assigned_nodes:
  888. private_node.entry.error_on_uninitialized = True
  889. pos, reduction = node.assignments[private_node.entry]
  890. if reduction:
  891. self.reductions.add(private_node.entry)
  892. node = self.visit_ForInStatNode(node)
  893. self.reductions = reductions
  894. return node
  895. def visit_ParallelWithBlockNode(self, node):
  896. for private_node in node.assigned_nodes:
  897. private_node.entry.error_on_uninitialized = True
  898. self._delete_privates(node)
  899. self.visitchildren(node)
  900. self._delete_privates(node)
  901. return node
  902. def visit_ForFromStatNode(self, node):
  903. condition_block = self.flow.nextblock()
  904. next_block = self.flow.newblock()
  905. # Condition with iterator
  906. self.flow.loops.append(LoopDescr(next_block, condition_block))
  907. self._visit(node.bound1)
  908. self._visit(node.bound2)
  909. if node.step is not None:
  910. self._visit(node.step)
  911. # Target assignment
  912. self.flow.nextblock()
  913. self.mark_assignment(node.target, node.bound1)
  914. if node.step is not None:
  915. self.mark_assignment(node.target, self.constant_folder(
  916. ExprNodes.binop_node(node.pos, '+', node.bound1, node.step)))
  917. # Body block
  918. self.flow.nextblock()
  919. self._visit(node.body)
  920. self.flow.loops.pop()
  921. # Loop it
  922. if self.flow.block:
  923. self.flow.block.add_child(condition_block)
  924. # Else clause
  925. if node.else_clause:
  926. self.flow.nextblock(parent=condition_block)
  927. self._visit(node.else_clause)
  928. if self.flow.block:
  929. self.flow.block.add_child(next_block)
  930. else:
  931. condition_block.add_child(next_block)
  932. if next_block.parents:
  933. self.flow.block = next_block
  934. else:
  935. self.flow.block = None
  936. return node
  937. def visit_LoopNode(self, node):
  938. raise InternalError("Generic loops are not supported")
  939. def visit_WithTargetAssignmentStatNode(self, node):
  940. self.mark_assignment(node.lhs, node.with_node.enter_call)
  941. return node
  942. def visit_WithStatNode(self, node):
  943. self._visit(node.manager)
  944. self._visit(node.enter_call)
  945. self._visit(node.body)
  946. return node
  947. def visit_TryExceptStatNode(self, node):
  948. # After exception handling
  949. next_block = self.flow.newblock()
  950. # Body block
  951. self.flow.newblock()
  952. # Exception entry point
  953. entry_point = self.flow.newblock()
  954. self.flow.exceptions.append(ExceptionDescr(entry_point))
  955. self.flow.nextblock()
  956. ## XXX: links to exception handling point should be added by
  957. ## XXX: children nodes
  958. self.flow.block.add_child(entry_point)
  959. self.flow.nextblock()
  960. self._visit(node.body)
  961. self.flow.exceptions.pop()
  962. # After exception
  963. if self.flow.block:
  964. if node.else_clause:
  965. self.flow.nextblock()
  966. self._visit(node.else_clause)
  967. if self.flow.block:
  968. self.flow.block.add_child(next_block)
  969. for clause in node.except_clauses:
  970. self.flow.block = entry_point
  971. if clause.pattern:
  972. for pattern in clause.pattern:
  973. self._visit(pattern)
  974. else:
  975. # TODO: handle * pattern
  976. pass
  977. entry_point = self.flow.newblock(parent=self.flow.block)
  978. self.flow.nextblock()
  979. if clause.target:
  980. self.mark_assignment(clause.target)
  981. self._visit(clause.body)
  982. if self.flow.block:
  983. self.flow.block.add_child(next_block)
  984. if self.flow.exceptions:
  985. entry_point.add_child(self.flow.exceptions[-1].entry_point)
  986. if next_block.parents:
  987. self.flow.block = next_block
  988. else:
  989. self.flow.block = None
  990. return node
  991. def visit_TryFinallyStatNode(self, node):
  992. body_block = self.flow.nextblock()
  993. # Exception entry point
  994. entry_point = self.flow.newblock()
  995. self.flow.block = entry_point
  996. self._visit(node.finally_except_clause)
  997. if self.flow.block and self.flow.exceptions:
  998. self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
  999. # Normal execution
  1000. finally_enter = self.flow.newblock()
  1001. self.flow.block = finally_enter
  1002. self._visit(node.finally_clause)
  1003. finally_exit = self.flow.block
  1004. descr = ExceptionDescr(entry_point, finally_enter, finally_exit)
  1005. self.flow.exceptions.append(descr)
  1006. if self.flow.loops:
  1007. self.flow.loops[-1].exceptions.append(descr)
  1008. self.flow.block = body_block
  1009. body_block.add_child(entry_point)
  1010. self.flow.nextblock()
  1011. self._visit(node.body)
  1012. self.flow.exceptions.pop()
  1013. if self.flow.loops:
  1014. self.flow.loops[-1].exceptions.pop()
  1015. if self.flow.block:
  1016. self.flow.block.add_child(finally_enter)
  1017. if finally_exit:
  1018. self.flow.block = self.flow.nextblock(parent=finally_exit)
  1019. else:
  1020. self.flow.block = None
  1021. return node
  1022. def visit_RaiseStatNode(self, node):
  1023. self.mark_position(node)
  1024. self.visitchildren(node)
  1025. if self.flow.exceptions:
  1026. self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
  1027. self.flow.block = None
  1028. return node
  1029. def visit_ReraiseStatNode(self, node):
  1030. self.mark_position(node)
  1031. if self.flow.exceptions:
  1032. self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
  1033. self.flow.block = None
  1034. return node
  1035. def visit_ReturnStatNode(self, node):
  1036. self.mark_position(node)
  1037. self.visitchildren(node)
  1038. outer_exception_handlers = iter(self.flow.exceptions[::-1])
  1039. for handler in outer_exception_handlers:
  1040. if handler.finally_enter:
  1041. self.flow.block.add_child(handler.finally_enter)
  1042. if handler.finally_exit:
  1043. # 'return' goes to function exit, or to the next outer 'finally' clause
  1044. exit_point = self.flow.exit_point
  1045. for next_handler in outer_exception_handlers:
  1046. if next_handler.finally_enter:
  1047. exit_point = next_handler.finally_enter
  1048. break
  1049. handler.finally_exit.add_child(exit_point)
  1050. break
  1051. else:
  1052. if self.flow.block:
  1053. self.flow.block.add_child(self.flow.exit_point)
  1054. self.flow.block = None
  1055. return node
  1056. def visit_BreakStatNode(self, node):
  1057. if not self.flow.loops:
  1058. #error(node.pos, "break statement not inside loop")
  1059. return node
  1060. loop = self.flow.loops[-1]
  1061. self.mark_position(node)
  1062. for exception in loop.exceptions[::-1]:
  1063. if exception.finally_enter:
  1064. self.flow.block.add_child(exception.finally_enter)
  1065. if exception.finally_exit:
  1066. exception.finally_exit.add_child(loop.next_block)
  1067. break
  1068. else:
  1069. self.flow.block.add_child(loop.next_block)
  1070. self.flow.block = None
  1071. return node
  1072. def visit_ContinueStatNode(self, node):
  1073. if not self.flow.loops:
  1074. #error(node.pos, "continue statement not inside loop")
  1075. return node
  1076. loop = self.flow.loops[-1]
  1077. self.mark_position(node)
  1078. for exception in loop.exceptions[::-1]:
  1079. if exception.finally_enter:
  1080. self.flow.block.add_child(exception.finally_enter)
  1081. if exception.finally_exit:
  1082. exception.finally_exit.add_child(loop.loop_block)
  1083. break
  1084. else:
  1085. self.flow.block.add_child(loop.loop_block)
  1086. self.flow.block = None
  1087. return node
  1088. def visit_ComprehensionNode(self, node):
  1089. if node.expr_scope:
  1090. self.env_stack.append(self.env)
  1091. self.env = node.expr_scope
  1092. # Skip append node here
  1093. self._visit(node.loop)
  1094. if node.expr_scope:
  1095. self.env = self.env_stack.pop()
  1096. return node
  1097. def visit_ScopedExprNode(self, node):
  1098. if node.expr_scope:
  1099. self.env_stack.append(self.env)
  1100. self.env = node.expr_scope
  1101. self.visitchildren(node)
  1102. if node.expr_scope:
  1103. self.env = self.env_stack.pop()
  1104. return node
  1105. def visit_PyClassDefNode(self, node):
  1106. self.visitchildren(node, ('dict', 'metaclass',
  1107. 'mkw', 'bases', 'class_result'))
  1108. self.flow.mark_assignment(node.target, node.classobj,
  1109. self.env.lookup(node.name))
  1110. self.env_stack.append(self.env)
  1111. self.env = node.scope
  1112. self.flow.nextblock()
  1113. self.visitchildren(node, ('body',))
  1114. self.flow.nextblock()
  1115. self.env = self.env_stack.pop()
  1116. return node
  1117. def visit_AmpersandNode(self, node):
  1118. if node.operand.is_name:
  1119. # Fake assignment to silence warning
  1120. self.mark_assignment(node.operand, fake_rhs_expr)
  1121. self.visitchildren(node)
  1122. return node