Cython 3.2.0__cp39-abi3-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. Cython/Build/BuildExecutable.py +169 -0
  2. Cython/Build/Cache.py +199 -0
  3. Cython/Build/Cythonize.py +350 -0
  4. Cython/Build/Dependencies.py +1314 -0
  5. Cython/Build/Distutils.py +1 -0
  6. Cython/Build/Inline.py +463 -0
  7. Cython/Build/IpythonMagic.py +560 -0
  8. Cython/Build/SharedModule.py +94 -0
  9. Cython/Build/Tests/TestCyCache.py +194 -0
  10. Cython/Build/Tests/TestCythonizeArgsParser.py +481 -0
  11. Cython/Build/Tests/TestDependencies.py +133 -0
  12. Cython/Build/Tests/TestInline.py +177 -0
  13. Cython/Build/Tests/TestIpythonMagic.py +287 -0
  14. Cython/Build/Tests/TestRecythonize.py +212 -0
  15. Cython/Build/Tests/TestStripLiterals.py +155 -0
  16. Cython/Build/Tests/__init__.py +1 -0
  17. Cython/Build/__init__.py +11 -0
  18. Cython/CodeWriter.py +815 -0
  19. Cython/Compiler/AnalysedTreeTransforms.py +97 -0
  20. Cython/Compiler/Annotate.py +328 -0
  21. Cython/Compiler/AutoDocTransforms.py +320 -0
  22. Cython/Compiler/Buffer.py +680 -0
  23. Cython/Compiler/Builtin.py +984 -0
  24. Cython/Compiler/CmdLine.py +263 -0
  25. Cython/Compiler/Code.pxd +149 -0
  26. Cython/Compiler/Code.py +3746 -0
  27. Cython/Compiler/Code.pyd +0 -0
  28. Cython/Compiler/CodeGeneration.py +33 -0
  29. Cython/Compiler/CythonScope.py +191 -0
  30. Cython/Compiler/Dataclass.py +864 -0
  31. Cython/Compiler/DebugFlags.py +24 -0
  32. Cython/Compiler/Errors.py +297 -0
  33. Cython/Compiler/ExprNodes.py +15562 -0
  34. Cython/Compiler/FlowControl.pxd +97 -0
  35. Cython/Compiler/FlowControl.py +1451 -0
  36. Cython/Compiler/FlowControl.pyd +0 -0
  37. Cython/Compiler/FusedNode.py +971 -0
  38. Cython/Compiler/FusedNode.pyd +0 -0
  39. Cython/Compiler/Future.py +16 -0
  40. Cython/Compiler/Interpreter.py +57 -0
  41. Cython/Compiler/Lexicon.py +421 -0
  42. Cython/Compiler/LineTable.py +114 -0
  43. Cython/Compiler/LineTable.pyd +0 -0
  44. Cython/Compiler/Main.py +857 -0
  45. Cython/Compiler/MatchCaseNodes.py +259 -0
  46. Cython/Compiler/MemoryView.py +905 -0
  47. Cython/Compiler/ModuleNode.py +4235 -0
  48. Cython/Compiler/Naming.py +363 -0
  49. Cython/Compiler/Nodes.py +10831 -0
  50. Cython/Compiler/Optimize.py +5288 -0
  51. Cython/Compiler/Options.py +843 -0
  52. Cython/Compiler/ParseTreeTransforms.pxd +78 -0
  53. Cython/Compiler/ParseTreeTransforms.py +4638 -0
  54. Cython/Compiler/Parsing.pxd +9 -0
  55. Cython/Compiler/Parsing.py +4775 -0
  56. Cython/Compiler/Parsing.pyd +0 -0
  57. Cython/Compiler/Pipeline.py +439 -0
  58. Cython/Compiler/PyrexTypes.py +5870 -0
  59. Cython/Compiler/Pythran.py +232 -0
  60. Cython/Compiler/Scanning.pxd +48 -0
  61. Cython/Compiler/Scanning.py +701 -0
  62. Cython/Compiler/Scanning.pyd +0 -0
  63. Cython/Compiler/StringEncoding.py +298 -0
  64. Cython/Compiler/Symtab.py +3073 -0
  65. Cython/Compiler/Tests/TestBuffer.py +105 -0
  66. Cython/Compiler/Tests/TestBuiltin.py +72 -0
  67. Cython/Compiler/Tests/TestCmdLine.py +586 -0
  68. Cython/Compiler/Tests/TestCode.py +144 -0
  69. Cython/Compiler/Tests/TestFlowControl.py +65 -0
  70. Cython/Compiler/Tests/TestGrammar.py +202 -0
  71. Cython/Compiler/Tests/TestMemView.py +71 -0
  72. Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
  73. Cython/Compiler/Tests/TestScanning.py +134 -0
  74. Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
  75. Cython/Compiler/Tests/TestStringEncoding.py +21 -0
  76. Cython/Compiler/Tests/TestTreeFragment.py +63 -0
  77. Cython/Compiler/Tests/TestTreePath.py +103 -0
  78. Cython/Compiler/Tests/TestTypes.py +75 -0
  79. Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
  80. Cython/Compiler/Tests/TestVisitor.py +61 -0
  81. Cython/Compiler/Tests/Utils.py +36 -0
  82. Cython/Compiler/Tests/__init__.py +1 -0
  83. Cython/Compiler/TreeFragment.py +278 -0
  84. Cython/Compiler/TreePath.py +303 -0
  85. Cython/Compiler/TypeInference.py +591 -0
  86. Cython/Compiler/TypeSlots.py +1174 -0
  87. Cython/Compiler/UFuncs.py +311 -0
  88. Cython/Compiler/UtilNodes.py +389 -0
  89. Cython/Compiler/UtilityCode.py +344 -0
  90. Cython/Compiler/Version.py +8 -0
  91. Cython/Compiler/Visitor.pxd +53 -0
  92. Cython/Compiler/Visitor.py +861 -0
  93. Cython/Compiler/Visitor.pyd +0 -0
  94. Cython/Compiler/__init__.py +1 -0
  95. Cython/Coverage.py +448 -0
  96. Cython/Debugger/Cygdb.py +177 -0
  97. Cython/Debugger/DebugWriter.py +82 -0
  98. Cython/Debugger/Tests/TestLibCython.py +275 -0
  99. Cython/Debugger/Tests/__init__.py +1 -0
  100. Cython/Debugger/Tests/cfuncs.c +8 -0
  101. Cython/Debugger/Tests/codefile +49 -0
  102. Cython/Debugger/Tests/test_libcython_in_gdb.py +578 -0
  103. Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
  104. Cython/Debugger/__init__.py +1 -0
  105. Cython/Debugger/libcython.py +1548 -0
  106. Cython/Debugger/libpython.py +2821 -0
  107. Cython/Debugging.py +20 -0
  108. Cython/Distutils/__init__.py +2 -0
  109. Cython/Distutils/build_ext.py +139 -0
  110. Cython/Distutils/extension.py +96 -0
  111. Cython/Distutils/old_build_ext.py +351 -0
  112. Cython/Includes/cpython/__init__.pxd +173 -0
  113. Cython/Includes/cpython/array.pxd +178 -0
  114. Cython/Includes/cpython/bool.pxd +37 -0
  115. Cython/Includes/cpython/buffer.pxd +112 -0
  116. Cython/Includes/cpython/bytearray.pxd +33 -0
  117. Cython/Includes/cpython/bytes.pxd +200 -0
  118. Cython/Includes/cpython/cellobject.pxd +35 -0
  119. Cython/Includes/cpython/ceval.pxd +8 -0
  120. Cython/Includes/cpython/codecs.pxd +121 -0
  121. Cython/Includes/cpython/complex.pxd +60 -0
  122. Cython/Includes/cpython/contextvars.pxd +145 -0
  123. Cython/Includes/cpython/conversion.pxd +36 -0
  124. Cython/Includes/cpython/datetime.pxd +395 -0
  125. Cython/Includes/cpython/descr.pxd +26 -0
  126. Cython/Includes/cpython/dict.pxd +187 -0
  127. Cython/Includes/cpython/exc.pxd +263 -0
  128. Cython/Includes/cpython/fileobject.pxd +57 -0
  129. Cython/Includes/cpython/float.pxd +47 -0
  130. Cython/Includes/cpython/function.pxd +65 -0
  131. Cython/Includes/cpython/genobject.pxd +25 -0
  132. Cython/Includes/cpython/getargs.pxd +12 -0
  133. Cython/Includes/cpython/instance.pxd +25 -0
  134. Cython/Includes/cpython/iterator.pxd +36 -0
  135. Cython/Includes/cpython/iterobject.pxd +24 -0
  136. Cython/Includes/cpython/list.pxd +92 -0
  137. Cython/Includes/cpython/long.pxd +149 -0
  138. Cython/Includes/cpython/longintrepr.pxd +14 -0
  139. Cython/Includes/cpython/mapping.pxd +63 -0
  140. Cython/Includes/cpython/marshal.pxd +66 -0
  141. Cython/Includes/cpython/mem.pxd +120 -0
  142. Cython/Includes/cpython/memoryview.pxd +50 -0
  143. Cython/Includes/cpython/method.pxd +49 -0
  144. Cython/Includes/cpython/module.pxd +208 -0
  145. Cython/Includes/cpython/number.pxd +258 -0
  146. Cython/Includes/cpython/object.pxd +433 -0
  147. Cython/Includes/cpython/pycapsule.pxd +143 -0
  148. Cython/Includes/cpython/pylifecycle.pxd +68 -0
  149. Cython/Includes/cpython/pyport.pxd +8 -0
  150. Cython/Includes/cpython/pystate.pxd +95 -0
  151. Cython/Includes/cpython/pythread.pxd +53 -0
  152. Cython/Includes/cpython/ref.pxd +141 -0
  153. Cython/Includes/cpython/sequence.pxd +134 -0
  154. Cython/Includes/cpython/set.pxd +119 -0
  155. Cython/Includes/cpython/slice.pxd +70 -0
  156. Cython/Includes/cpython/time.pxd +129 -0
  157. Cython/Includes/cpython/tuple.pxd +72 -0
  158. Cython/Includes/cpython/type.pxd +53 -0
  159. Cython/Includes/cpython/unicode.pxd +639 -0
  160. Cython/Includes/cpython/version.pxd +32 -0
  161. Cython/Includes/cpython/weakref.pxd +78 -0
  162. Cython/Includes/libc/__init__.pxd +1 -0
  163. Cython/Includes/libc/complex.pxd +35 -0
  164. Cython/Includes/libc/errno.pxd +127 -0
  165. Cython/Includes/libc/float.pxd +43 -0
  166. Cython/Includes/libc/limits.pxd +28 -0
  167. Cython/Includes/libc/locale.pxd +46 -0
  168. Cython/Includes/libc/math.pxd +209 -0
  169. Cython/Includes/libc/setjmp.pxd +10 -0
  170. Cython/Includes/libc/signal.pxd +64 -0
  171. Cython/Includes/libc/stddef.pxd +9 -0
  172. Cython/Includes/libc/stdint.pxd +105 -0
  173. Cython/Includes/libc/stdio.pxd +80 -0
  174. Cython/Includes/libc/stdlib.pxd +72 -0
  175. Cython/Includes/libc/string.pxd +50 -0
  176. Cython/Includes/libc/threads.pxd +234 -0
  177. Cython/Includes/libc/time.pxd +52 -0
  178. Cython/Includes/libcpp/__init__.pxd +4 -0
  179. Cython/Includes/libcpp/algorithm.pxd +320 -0
  180. Cython/Includes/libcpp/any.pxd +16 -0
  181. Cython/Includes/libcpp/atomic.pxd +59 -0
  182. Cython/Includes/libcpp/barrier.pxd +22 -0
  183. Cython/Includes/libcpp/bit.pxd +29 -0
  184. Cython/Includes/libcpp/cast.pxd +12 -0
  185. Cython/Includes/libcpp/cmath.pxd +518 -0
  186. Cython/Includes/libcpp/complex.pxd +106 -0
  187. Cython/Includes/libcpp/condition_variable.pxd +322 -0
  188. Cython/Includes/libcpp/deque.pxd +165 -0
  189. Cython/Includes/libcpp/exception.pxd +86 -0
  190. Cython/Includes/libcpp/execution.pxd +15 -0
  191. Cython/Includes/libcpp/forward_list.pxd +63 -0
  192. Cython/Includes/libcpp/functional.pxd +26 -0
  193. Cython/Includes/libcpp/future.pxd +103 -0
  194. Cython/Includes/libcpp/iterator.pxd +34 -0
  195. Cython/Includes/libcpp/latch.pxd +17 -0
  196. Cython/Includes/libcpp/limits.pxd +61 -0
  197. Cython/Includes/libcpp/list.pxd +117 -0
  198. Cython/Includes/libcpp/map.pxd +252 -0
  199. Cython/Includes/libcpp/memory.pxd +115 -0
  200. Cython/Includes/libcpp/mutex.pxd +387 -0
  201. Cython/Includes/libcpp/numbers.pxd +15 -0
  202. Cython/Includes/libcpp/numeric.pxd +131 -0
  203. Cython/Includes/libcpp/optional.pxd +34 -0
  204. Cython/Includes/libcpp/pair.pxd +1 -0
  205. Cython/Includes/libcpp/queue.pxd +25 -0
  206. Cython/Includes/libcpp/random.pxd +166 -0
  207. Cython/Includes/libcpp/semaphore.pxd +43 -0
  208. Cython/Includes/libcpp/set.pxd +228 -0
  209. Cython/Includes/libcpp/shared_mutex.pxd +96 -0
  210. Cython/Includes/libcpp/span.pxd +87 -0
  211. Cython/Includes/libcpp/stack.pxd +11 -0
  212. Cython/Includes/libcpp/stop_token.pxd +117 -0
  213. Cython/Includes/libcpp/string.pxd +355 -0
  214. Cython/Includes/libcpp/string_view.pxd +183 -0
  215. Cython/Includes/libcpp/typeindex.pxd +15 -0
  216. Cython/Includes/libcpp/typeinfo.pxd +10 -0
  217. Cython/Includes/libcpp/unordered_map.pxd +193 -0
  218. Cython/Includes/libcpp/unordered_set.pxd +152 -0
  219. Cython/Includes/libcpp/utility.pxd +30 -0
  220. Cython/Includes/libcpp/vector.pxd +186 -0
  221. Cython/Includes/numpy/math.pxd +150 -0
  222. Cython/Includes/openmp.pxd +50 -0
  223. Cython/Includes/posix/__init__.pxd +1 -0
  224. Cython/Includes/posix/dlfcn.pxd +14 -0
  225. Cython/Includes/posix/fcntl.pxd +86 -0
  226. Cython/Includes/posix/ioctl.pxd +4 -0
  227. Cython/Includes/posix/mman.pxd +101 -0
  228. Cython/Includes/posix/resource.pxd +57 -0
  229. Cython/Includes/posix/select.pxd +21 -0
  230. Cython/Includes/posix/signal.pxd +73 -0
  231. Cython/Includes/posix/stat.pxd +98 -0
  232. Cython/Includes/posix/stdio.pxd +37 -0
  233. Cython/Includes/posix/stdlib.pxd +29 -0
  234. Cython/Includes/posix/strings.pxd +9 -0
  235. Cython/Includes/posix/time.pxd +71 -0
  236. Cython/Includes/posix/types.pxd +30 -0
  237. Cython/Includes/posix/uio.pxd +26 -0
  238. Cython/Includes/posix/unistd.pxd +271 -0
  239. Cython/Includes/posix/wait.pxd +38 -0
  240. Cython/Plex/Actions.pxd +24 -0
  241. Cython/Plex/Actions.py +119 -0
  242. Cython/Plex/Actions.pyd +0 -0
  243. Cython/Plex/DFA.pxd +14 -0
  244. Cython/Plex/DFA.py +164 -0
  245. Cython/Plex/DFA.pyd +0 -0
  246. Cython/Plex/Errors.py +48 -0
  247. Cython/Plex/Lexicons.py +178 -0
  248. Cython/Plex/Machines.pxd +36 -0
  249. Cython/Plex/Machines.py +238 -0
  250. Cython/Plex/Machines.pyd +0 -0
  251. Cython/Plex/Regexps.py +535 -0
  252. Cython/Plex/Scanners.pxd +45 -0
  253. Cython/Plex/Scanners.py +328 -0
  254. Cython/Plex/Scanners.pyd +0 -0
  255. Cython/Plex/Transitions.pxd +14 -0
  256. Cython/Plex/Transitions.py +239 -0
  257. Cython/Plex/Transitions.pyd +0 -0
  258. Cython/Plex/__init__.py +34 -0
  259. Cython/Runtime/__init__.py +1 -0
  260. Cython/Runtime/refnanny.pyd +0 -0
  261. Cython/Runtime/refnanny.pyx +237 -0
  262. Cython/Shadow.py +690 -0
  263. Cython/Shadow.pyi +521 -0
  264. Cython/StringIOTree.py +170 -0
  265. Cython/StringIOTree.pyd +0 -0
  266. Cython/Tempita/__init__.py +4 -0
  267. Cython/Tempita/_looper.py +154 -0
  268. Cython/Tempita/_tempita.py +1091 -0
  269. Cython/Tempita/_tempita.pyd +0 -0
  270. Cython/TestUtils.py +422 -0
  271. Cython/Tests/TestCodeWriter.py +128 -0
  272. Cython/Tests/TestCythonUtils.py +202 -0
  273. Cython/Tests/TestJediTyper.py +223 -0
  274. Cython/Tests/TestShadow.py +114 -0
  275. Cython/Tests/TestStringIOTree.py +67 -0
  276. Cython/Tests/TestTestUtils.py +90 -0
  277. Cython/Tests/__init__.py +1 -0
  278. Cython/Tests/xmlrunner.py +390 -0
  279. Cython/Utility/AsyncGen.c +1031 -0
  280. Cython/Utility/Buffer.c +865 -0
  281. Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
  282. Cython/Utility/Builtins.c +810 -0
  283. Cython/Utility/CConvert.pyx +134 -0
  284. Cython/Utility/CMath.c +104 -0
  285. Cython/Utility/CommonStructures.c +226 -0
  286. Cython/Utility/Complex.c +378 -0
  287. Cython/Utility/Coroutine.c +2300 -0
  288. Cython/Utility/CpdefEnums.pyx +103 -0
  289. Cython/Utility/CppConvert.pyx +282 -0
  290. Cython/Utility/CppSupport.cpp +151 -0
  291. Cython/Utility/CythonFunction.c +1832 -0
  292. Cython/Utility/Dataclasses.c +101 -0
  293. Cython/Utility/Embed.c +121 -0
  294. Cython/Utility/Exceptions.c +1016 -0
  295. Cython/Utility/ExtensionTypes.c +996 -0
  296. Cython/Utility/FunctionArguments.c +1043 -0
  297. Cython/Utility/FusedFunction.pyx +44 -0
  298. Cython/Utility/ImportExport.c +907 -0
  299. Cython/Utility/MemoryView.pxd +188 -0
  300. Cython/Utility/MemoryView.pyx +1482 -0
  301. Cython/Utility/MemoryView_C.c +927 -0
  302. Cython/Utility/ModuleSetupCode.c +3203 -0
  303. Cython/Utility/NumpyImportArray.c +46 -0
  304. Cython/Utility/ObjectHandling.c +3273 -0
  305. Cython/Utility/Optimize.c +1603 -0
  306. Cython/Utility/Overflow.c +384 -0
  307. Cython/Utility/Printing.c +86 -0
  308. Cython/Utility/Profile.c +732 -0
  309. Cython/Utility/StringTools.c +1379 -0
  310. Cython/Utility/Synchronization.c +399 -0
  311. Cython/Utility/TString.c +356 -0
  312. Cython/Utility/TestCyUtilityLoader.pyx +8 -0
  313. Cython/Utility/TestCythonScope.pyx +75 -0
  314. Cython/Utility/TestUtilityLoader.c +12 -0
  315. Cython/Utility/TypeConversion.c +1385 -0
  316. Cython/Utility/UFuncs.pyx +50 -0
  317. Cython/Utility/UFuncs_C.c +89 -0
  318. Cython/Utility/__init__.py +28 -0
  319. Cython/Utility/arrayarray.h +167 -0
  320. Cython/Utils.py +687 -0
  321. Cython/Utils.pyd +0 -0
  322. Cython/__init__.py +10 -0
  323. Cython/__init__.pyi +7 -0
  324. Cython/py.typed +0 -0
  325. cython-3.2.0.dist-info/METADATA +85 -0
  326. cython-3.2.0.dist-info/RECORD +333 -0
  327. cython-3.2.0.dist-info/WHEEL +5 -0
  328. cython-3.2.0.dist-info/entry_points.txt +4 -0
  329. cython-3.2.0.dist-info/top_level.txt +3 -0
  330. cython.py +29 -0
  331. pyximport/__init__.py +4 -0
  332. pyximport/pyxbuild.py +160 -0
  333. pyximport/pyximport.py +482 -0
@@ -0,0 +1,1451 @@
1
+ # cython: auto_pickle=True
2
+
3
+
4
+ import cython
5
+ cython.declare(PyrexTypes=object, ExprNodes=object, Nodes=object, Builtin=object,
6
+ Options=object, TreeVisitor=object, CythonTransform=object,
7
+ InternalError=object, error=object, warning=object,
8
+ fake_rhs_expr=object, TypedExprNode=object)
9
+
10
+ from . import Builtin
11
+ from . import ExprNodes
12
+ from . import Nodes
13
+ from . import Options
14
+ from . import PyrexTypes
15
+
16
+ from .Visitor import TreeVisitor, CythonTransform
17
+ from .Errors import error, warning, InternalError
18
+
19
+
20
+ class TypedExprNode(ExprNodes.ExprNode):
21
+ # Used for declaring assignments of a specified type without a known entry.
22
+ def __init__(self, type, may_be_none=None, pos=None):
23
+ super().__init__(pos)
24
+ self.type = type
25
+ self._may_be_none = may_be_none
26
+
27
+ def may_be_none(self):
28
+ return self._may_be_none != False
29
+
30
+ # Fake rhs to silence "unused variable" warning
31
+ fake_rhs_expr = TypedExprNode(PyrexTypes.unspecified_type)
32
+
33
+
34
+ class ControlBlock:
35
+ """Control flow graph node. Sequence of assignments and name references.
36
+
37
+ children set of children nodes
38
+ parents set of parent nodes
39
+ positions set of position markers
40
+
41
+ stats list of block statements
42
+ gen dict of assignments generated by this block
43
+ bounded set of entries that are definitely bounded in this block
44
+
45
+ Example:
46
+
47
+ a = 1
48
+ b = a + c # 'c' is already bounded or exception here
49
+
50
+ stats = [Assignment(a), NameReference(a), NameReference(c),
51
+ Assignment(b)]
52
+ gen = {Entry(a): Assignment(a), Entry(b): Assignment(b)}
53
+ bounded = {Entry(a), Entry(c)}
54
+
55
+ """
56
+
57
+ def __init__(self):
58
+ self.children = set()
59
+ self.parents = set()
60
+ self.positions = set()
61
+
62
+ self.stats = []
63
+ self.gen = {}
64
+ self.bounded = set()
65
+
66
+ self.i_input = 0
67
+ self.i_output = 0
68
+ self.i_gen = 0
69
+ self.i_kill = 0
70
+ self.i_state = 0
71
+
72
+ def empty(self):
73
+ return (not self.stats and not self.positions)
74
+
75
+ def detach(self):
76
+ """Detach block from parents and children."""
77
+ for child in self.children:
78
+ child.parents.remove(self)
79
+ for parent in self.parents:
80
+ parent.children.remove(self)
81
+ self.parents.clear()
82
+ self.children.clear()
83
+
84
+ def add_child(self, block):
85
+ self.children.add(block)
86
+ block.parents.add(self)
87
+
88
+
89
+ class ExitBlock(ControlBlock):
90
+ """Non-empty exit point block."""
91
+
92
+ def empty(self):
93
+ return False
94
+
95
+
96
+ class AssignmentList:
97
+ def __init__(self):
98
+ self.stats = []
99
+
100
+
101
+ class ControlFlow:
102
+ """Control-flow graph.
103
+
104
+ entry_point ControlBlock entry point for this graph
105
+ exit_point ControlBlock normal exit point
106
+ block ControlBlock current block
107
+ blocks set children nodes
108
+ entries set tracked entries
109
+ loops list stack for loop descriptors
110
+ exceptions list stack for exception descriptors
111
+ in_try_block int track if we're in a try...except or try...finally block
112
+ """
113
+
114
+ def __init__(self):
115
+ self.blocks = set()
116
+ self.entries = set()
117
+ self.loops = []
118
+ self.exceptions = []
119
+
120
+ self.entry_point = ControlBlock()
121
+ self.exit_point = ExitBlock()
122
+ self.blocks.add(self.exit_point)
123
+ self.block = self.entry_point
124
+ self.in_try_block = 0
125
+
126
+ def newblock(self, parent=None):
127
+ """Create floating block linked to `parent` if given.
128
+
129
+ NOTE: Block is NOT added to self.blocks
130
+ """
131
+ block = ControlBlock()
132
+ self.blocks.add(block)
133
+ if parent:
134
+ parent.add_child(block)
135
+ return block
136
+
137
+ def nextblock(self, parent=None):
138
+ """Create block children block linked to current or `parent` if given.
139
+
140
+ NOTE: Block is added to self.blocks
141
+ """
142
+ block = ControlBlock()
143
+ self.blocks.add(block)
144
+ if parent:
145
+ parent.add_child(block)
146
+ elif self.block:
147
+ self.block.add_child(block)
148
+ self.block = block
149
+ return self.block
150
+
151
+ def is_tracked(self, entry):
152
+ if entry.is_anonymous:
153
+ return False
154
+ return (entry.is_local or entry.is_pyclass_attr or entry.is_arg or
155
+ entry.from_closure or entry.in_closure or
156
+ entry.error_on_uninitialized)
157
+
158
+ def is_statically_assigned(self, entry):
159
+ if (entry.is_local and entry.is_variable and
160
+ (entry.type.is_struct_or_union or
161
+ entry.type.is_complex or
162
+ entry.type.is_array or
163
+ entry.type.is_cython_lock_type or
164
+ (entry.type.is_cpp_class and not entry.is_cpp_optional))):
165
+ # stack allocated structured variable => never uninitialised
166
+ return True
167
+ return False
168
+
169
+ def mark_position(self, node):
170
+ """Mark position, will be used to draw graph nodes."""
171
+ if self.block:
172
+ self.block.positions.add(node.pos[:2])
173
+
174
+ def mark_assignment(self, lhs, rhs, entry, rhs_scope=None):
175
+ if self.block and self.is_tracked(entry):
176
+ assignment = NameAssignment(lhs, rhs, entry, rhs_scope=rhs_scope)
177
+ self.block.stats.append(assignment)
178
+ self.block.gen[entry] = assignment
179
+ self.entries.add(entry)
180
+
181
+ def mark_argument(self, lhs, rhs, entry):
182
+ if self.block and self.is_tracked(entry):
183
+ assignment = Argument(lhs, rhs, entry)
184
+ self.block.stats.append(assignment)
185
+ self.block.gen[entry] = assignment
186
+ self.entries.add(entry)
187
+
188
+ def mark_deletion(self, node, entry):
189
+ if self.block and self.is_tracked(entry):
190
+ assignment = NameDeletion(node, entry)
191
+ self.block.stats.append(assignment)
192
+ self.block.gen[entry] = Uninitialized
193
+ self.entries.add(entry)
194
+
195
+ def mark_reference(self, node, entry):
196
+ if self.block and self.is_tracked(entry):
197
+ self.block.stats.append(NameReference(node, entry))
198
+ ## XXX: We don't track expression evaluation order so we can't use
199
+ ## XXX: successful reference as initialization sign.
200
+ ## # Local variable is definitely bound after this reference
201
+ ## if not node.allow_null:
202
+ ## self.block.bounded.add(entry)
203
+ self.entries.add(entry)
204
+
205
+ def normalize(self):
206
+ """Delete unreachable and orphan blocks."""
207
+ queue = {self.entry_point}
208
+ visited = set()
209
+ while queue:
210
+ root = queue.pop()
211
+ visited.add(root)
212
+ for child in root.children:
213
+ if child not in visited:
214
+ queue.add(child)
215
+
216
+ unreachable: set = self.blocks - visited
217
+ block: ControlBlock
218
+ for block in unreachable:
219
+ block.detach()
220
+
221
+ visited.remove(self.entry_point)
222
+
223
+ parent: ControlBlock
224
+ for block in visited:
225
+ if block.empty():
226
+ for parent in block.parents: # Re-parent
227
+ for child in block.children:
228
+ parent.add_child(child)
229
+ block.detach()
230
+ unreachable.add(block)
231
+ self.blocks -= unreachable
232
+
233
+ def initialize(self):
234
+ """Set initial state, map assignments to bits."""
235
+ self.assmts = {}
236
+ assmts: AssignmentList
237
+ block: ControlBlock
238
+
239
+ bit: int = 1
240
+ for entry in self.entries:
241
+ assmts = AssignmentList()
242
+ assmts.mask = assmts.bit = bit
243
+ self.assmts[entry] = assmts
244
+ bit <<= 1
245
+
246
+ for block in self.blocks:
247
+ for stat in block.stats:
248
+ if isinstance(stat, NameAssignment):
249
+ stat.bit = bit
250
+ assmts = self.assmts[stat.entry]
251
+ assmts.stats.append(stat)
252
+ assmts.mask |= bit
253
+ bit <<= 1
254
+
255
+ for block in self.blocks:
256
+ for entry, stat in block.gen.items():
257
+ assmts = self.assmts[entry]
258
+ if stat is Uninitialized:
259
+ block.i_gen |= assmts.bit
260
+ else:
261
+ block.i_gen |= stat.bit
262
+ block.i_kill |= assmts.mask
263
+ block.i_output = block.i_gen
264
+ for entry in block.bounded:
265
+ block.i_kill |= self.assmts[entry].bit
266
+
267
+ for assmts in self.assmts.values():
268
+ self.entry_point.i_gen |= assmts.bit
269
+ self.entry_point.i_output = self.entry_point.i_gen
270
+
271
+ def map_one(self, istate, entry):
272
+ ret = set()
273
+ assmts: AssignmentList = self.assmts[entry]
274
+ if istate & assmts.bit:
275
+ if self.is_statically_assigned(entry):
276
+ ret.add(StaticAssignment(entry))
277
+ elif entry.from_closure:
278
+ ret.add(Unknown)
279
+ else:
280
+ ret.add(Uninitialized)
281
+
282
+ assmt: NameAssignment
283
+ for assmt in assmts.stats:
284
+ if istate & assmt.bit:
285
+ ret.add(assmt)
286
+ return ret
287
+
288
+ def reaching_definitions(self):
289
+ """Per-block reaching definitions analysis."""
290
+ block: ControlBlock
291
+ parent: ControlBlock
292
+
293
+ dirty = True
294
+ while dirty:
295
+ dirty = False
296
+ for block in self.blocks:
297
+ i_input = 0
298
+ for parent in block.parents:
299
+ i_input |= parent.i_output
300
+ i_output = (i_input & ~block.i_kill) | block.i_gen
301
+ if i_output != block.i_output:
302
+ dirty = True
303
+ block.i_input = i_input
304
+ block.i_output = i_output
305
+
306
+
307
+ class LoopDescr:
308
+ def __init__(self, next_block, loop_block):
309
+ self.next_block = next_block
310
+ self.loop_block = loop_block
311
+ self.exceptions = []
312
+
313
+
314
+ class ExceptionDescr:
315
+ """Exception handling helper.
316
+
317
+ entry_point ControlBlock Exception handling entry point
318
+ finally_enter ControlBlock Normal finally clause entry point
319
+ finally_exit ControlBlock Normal finally clause exit point
320
+ """
321
+
322
+ def __init__(self, entry_point, finally_enter=None, finally_exit=None):
323
+ self.entry_point = entry_point
324
+ self.finally_enter = finally_enter
325
+ self.finally_exit = finally_exit
326
+
327
+
328
+ class NameAssignment:
329
+ def __init__(self, lhs, rhs, entry, rhs_scope=None):
330
+ if lhs.cf_state is None:
331
+ lhs.cf_state = set()
332
+ self.lhs = lhs
333
+ self.rhs = rhs
334
+ self.entry = entry
335
+ self.pos = lhs.pos
336
+ self.refs = set()
337
+ self.is_arg = False
338
+ self.is_deletion = False
339
+ self.inferred_type = None
340
+ # For generator expression targets, the rhs can have a different scope than the lhs.
341
+ self.rhs_scope = rhs_scope
342
+
343
+ def __repr__(self):
344
+ return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
345
+
346
+ def infer_type(self):
347
+ self.inferred_type = self.rhs.infer_type(self.rhs_scope or self.entry.scope)
348
+ return self.inferred_type
349
+
350
+ def type_dependencies(self):
351
+ return self.rhs.type_dependencies(self.rhs_scope or self.entry.scope)
352
+
353
+ @property
354
+ def type(self):
355
+ if not self.entry.type.is_unspecified:
356
+ return self.entry.type
357
+ return self.inferred_type
358
+
359
+
360
+ class StaticAssignment(NameAssignment):
361
+ """Initialised at declaration time, e.g. stack allocation."""
362
+ def __init__(self, entry):
363
+ if not entry.type.is_pyobject:
364
+ may_be_none = False
365
+ else:
366
+ may_be_none = None # unknown
367
+ lhs = TypedExprNode(
368
+ entry.type, may_be_none=may_be_none, pos=entry.pos)
369
+ super().__init__(lhs, lhs, entry)
370
+
371
+ def infer_type(self):
372
+ return self.entry.type
373
+
374
+ def type_dependencies(self):
375
+ return ()
376
+
377
+
378
+ class Argument(NameAssignment):
379
+ def __init__(self, lhs, rhs, entry):
380
+ NameAssignment.__init__(self, lhs, rhs, entry)
381
+ self.is_arg = True
382
+
383
+
384
+ class NameDeletion(NameAssignment):
385
+ def __init__(self, lhs, entry):
386
+ NameAssignment.__init__(self, lhs, lhs, entry)
387
+ self.is_deletion = True
388
+
389
+ def infer_type(self):
390
+ inferred_type = self.rhs.infer_type(self.entry.scope)
391
+ if (not inferred_type.is_pyobject
392
+ and inferred_type.can_coerce_to_pyobject(self.entry.scope)):
393
+ return PyrexTypes.py_object_type
394
+ self.inferred_type = inferred_type
395
+ return inferred_type
396
+
397
+
398
+ class Uninitialized:
399
+ """Definitely not initialised yet."""
400
+
401
+
402
+ class Unknown:
403
+ """Coming from outer closure, might be initialised or not."""
404
+
405
+
406
+ class NameReference:
407
+ def __init__(self, node, entry):
408
+ if node.cf_state is None:
409
+ node.cf_state = set()
410
+ self.node = node
411
+ self.entry = entry
412
+ self.pos = node.pos
413
+
414
+ def __repr__(self):
415
+ return '%s(entry=%r)' % (self.__class__.__name__, self.entry)
416
+
417
+
418
+ class ControlFlowState(list):
419
+ # Keeps track of Node's entry assignments
420
+ #
421
+ # cf_is_null [boolean] It is uninitialized
422
+ # cf_maybe_null [boolean] May be uninitialized
423
+ # is_single [boolean] Has only one assignment at this point
424
+
425
+ cf_maybe_null = False
426
+ cf_is_null = False
427
+ is_single = False
428
+
429
+ def __init__(self, state):
430
+ if Uninitialized in state:
431
+ state.discard(Uninitialized)
432
+ self.cf_maybe_null = True
433
+ if not state:
434
+ self.cf_is_null = True
435
+ elif Unknown in state:
436
+ state.discard(Unknown)
437
+ self.cf_maybe_null = True
438
+ else:
439
+ if len(state) == 1:
440
+ self.is_single = True
441
+ # XXX: Remove fake_rhs_expr
442
+ super().__init__(
443
+ [i for i in state if i.rhs is not fake_rhs_expr])
444
+
445
+ def one(self):
446
+ return self[0]
447
+
448
+
449
+ class GVContext:
450
+ """Graphviz subgraph object."""
451
+
452
+ def __init__(self):
453
+ self.blockids = {}
454
+ self.nextid = 0
455
+ self.children = []
456
+ self.sources = {}
457
+
458
+ def add(self, child):
459
+ self.children.append(child)
460
+
461
+ def nodeid(self, block):
462
+ if block not in self.blockids:
463
+ self.blockids[block] = 'block%d' % self.nextid
464
+ self.nextid += 1
465
+ return self.blockids[block]
466
+
467
+ def extract_sources(self, block):
468
+ if not block.positions:
469
+ return ''
470
+ start = min(block.positions)
471
+ stop = max(block.positions)
472
+ srcdescr = start[0]
473
+ if srcdescr not in self.sources:
474
+ self.sources[srcdescr] = list(srcdescr.get_lines())
475
+ lines = self.sources[srcdescr]
476
+ return '\\n'.join([l.strip() for l in lines[start[1] - 1:stop[1]]])
477
+
478
+ def render(self, fp, name, annotate_defs=False):
479
+ """Render graphviz dot graph"""
480
+ fp.write('digraph %s {\n' % name)
481
+ fp.write(' node [shape=box];\n')
482
+ for child in self.children:
483
+ child.render(fp, self, annotate_defs)
484
+ fp.write('}\n')
485
+
486
+ def escape(self, text):
487
+ return text.replace('"', '\\"').replace('\n', '\\n')
488
+
489
+
490
+ class GV:
491
+ """Graphviz DOT renderer."""
492
+
493
+ def __init__(self, name, flow):
494
+ self.name = name
495
+ self.flow = flow
496
+
497
+ def render(self, fp, ctx, annotate_defs=False):
498
+ fp.write(' subgraph %s {\n' % self.name)
499
+ for block in self.flow.blocks:
500
+ label = ctx.extract_sources(block)
501
+ if annotate_defs:
502
+ for stat in block.stats:
503
+ if isinstance(stat, NameAssignment):
504
+ label += '\n %s [%s %s]' % (
505
+ stat.entry.name, 'deletion' if stat.is_deletion else 'definition', stat.pos[1])
506
+ elif isinstance(stat, NameReference):
507
+ if stat.entry:
508
+ label += '\n %s [reference %s]' % (stat.entry.name, stat.pos[1])
509
+ if not label:
510
+ label = 'empty'
511
+ pid = ctx.nodeid(block)
512
+ fp.write(' %s [label="%s"];\n' % (pid, ctx.escape(label)))
513
+ for block in self.flow.blocks:
514
+ pid = ctx.nodeid(block)
515
+ for child in block.children:
516
+ fp.write(' %s -> %s;\n' % (pid, ctx.nodeid(child)))
517
+ fp.write(' }\n')
518
+
519
+
520
+ class MessageCollection:
521
+ """Collect error/warnings messages first then sort"""
522
+ def __init__(self):
523
+ self.messages = set()
524
+
525
+ def error(self, pos, message):
526
+ self.messages.add((pos, True, message))
527
+
528
+ def warning(self, pos, message):
529
+ self.messages.add((pos, False, message))
530
+
531
+ def report(self):
532
+ for pos, is_error, message in sorted(self.messages):
533
+ if is_error:
534
+ error(pos, message)
535
+ else:
536
+ warning(pos, message, 2)
537
+
538
+
539
+ @cython.cfunc
540
+ def check_definitions(flow: ControlFlow, compiler_directives: dict):
541
+ flow.initialize()
542
+ flow.reaching_definitions()
543
+
544
+ # Track down state
545
+ assignments = set()
546
+ # Node to entry map
547
+ references = {}
548
+ assmt_nodes = set()
549
+
550
+ block: ControlBlock
551
+ assmt: NameAssignment
552
+ for block in flow.blocks:
553
+ i_state = block.i_input
554
+ for stat in block.stats:
555
+ i_assmts = flow.assmts[stat.entry]
556
+ state = flow.map_one(i_state, stat.entry)
557
+ if isinstance(stat, NameAssignment):
558
+ stat.lhs.cf_state.update(state)
559
+ assmt_nodes.add(stat.lhs)
560
+ i_state = i_state & ~i_assmts.mask
561
+ if stat.is_deletion:
562
+ i_state |= i_assmts.bit
563
+ else:
564
+ i_state |= stat.bit
565
+ assignments.add(stat)
566
+ if stat.rhs is not fake_rhs_expr:
567
+ stat.entry.cf_assignments.append(stat)
568
+ elif isinstance(stat, NameReference):
569
+ references[stat.node] = stat.entry
570
+ stat.entry.cf_references.append(stat)
571
+ stat.node.cf_state.update(state)
572
+ ## if not stat.node.allow_null:
573
+ ## i_state &= ~i_assmts.bit
574
+ ## # after successful read, the state is known to be initialised
575
+ state.discard(Uninitialized)
576
+ state.discard(Unknown)
577
+ for assmt in state:
578
+ assmt.refs.add(stat)
579
+
580
+ # Check variable usage
581
+ warn_maybe_uninitialized = compiler_directives['warn.maybe_uninitialized']
582
+ warn_unused_result = compiler_directives['warn.unused_result']
583
+ warn_unused = compiler_directives['warn.unused']
584
+ warn_unused_arg = compiler_directives['warn.unused_arg']
585
+
586
+ messages = MessageCollection()
587
+
588
+ # assignment hints
589
+ for node in assmt_nodes:
590
+ if Uninitialized in node.cf_state:
591
+ node.cf_maybe_null = True
592
+ if len(node.cf_state) == 1:
593
+ node.cf_is_null = True
594
+ else:
595
+ node.cf_is_null = False
596
+ elif Unknown in node.cf_state:
597
+ node.cf_maybe_null = True
598
+ else:
599
+ node.cf_is_null = False
600
+ node.cf_maybe_null = False
601
+
602
+ # Find uninitialized references and cf-hints
603
+ for node, entry in references.items():
604
+ if Uninitialized in node.cf_state:
605
+ node.cf_maybe_null = True
606
+ if (not entry.from_closure and len(node.cf_state) == 1
607
+ and entry.name not in entry.scope.scope_predefined_names):
608
+ node.cf_is_null = True
609
+ if (node.allow_null or entry.from_closure
610
+ or entry.is_pyclass_attr or entry.type.is_error):
611
+ pass # Can be uninitialized here
612
+ elif node.cf_is_null and not entry.in_closure:
613
+ if entry.error_on_uninitialized or (
614
+ Options.error_on_uninitialized and (
615
+ entry.type.is_pyobject or entry.type.is_unspecified)):
616
+ messages.error(
617
+ node.pos,
618
+ "local variable '%s' referenced before assignment"
619
+ % entry.name)
620
+ else:
621
+ messages.warning(
622
+ node.pos,
623
+ "local variable '%s' referenced before assignment"
624
+ % entry.name)
625
+ elif warn_maybe_uninitialized:
626
+ msg = "local variable '%s' might be referenced before assignment" % entry.name
627
+ if entry.in_closure:
628
+ msg += " (maybe initialized inside a closure)"
629
+ messages.warning(
630
+ node.pos,
631
+ msg)
632
+ elif Unknown in node.cf_state:
633
+ # TODO: better cross-closure analysis to know when inner functions
634
+ # are being called before a variable is being set, and when
635
+ # a variable is known to be set before even defining the
636
+ # inner function, etc.
637
+ node.cf_maybe_null = True
638
+ else:
639
+ node.cf_is_null = False
640
+ node.cf_maybe_null = False
641
+
642
+ # Unused result
643
+ for assmt in assignments:
644
+ if (not assmt.refs and not assmt.entry.is_pyclass_attr
645
+ and not assmt.entry.in_closure):
646
+ if assmt.entry.cf_references and warn_unused_result:
647
+ if assmt.is_arg:
648
+ messages.warning(assmt.pos, "Unused argument value '%s'" %
649
+ assmt.entry.name)
650
+ else:
651
+ messages.warning(assmt.pos, "Unused result in '%s'" %
652
+ assmt.entry.name)
653
+ assmt.lhs.cf_used = False
654
+
655
+ # Unused entries
656
+ for entry in flow.entries:
657
+ if (not entry.cf_references
658
+ and not entry.is_pyclass_attr):
659
+ if entry.name != '_' and not entry.name.startswith('unused'):
660
+ # '_' is often used for unused variables, e.g. in loops
661
+ if entry.is_arg:
662
+ if warn_unused_arg:
663
+ messages.warning(entry.pos, "Unused argument '%s'" %
664
+ entry.name)
665
+ else:
666
+ if warn_unused:
667
+ messages.warning(entry.pos, "Unused entry '%s'" %
668
+ entry.name)
669
+ entry.cf_used = False
670
+
671
+ messages.report()
672
+
673
+ for node in assmt_nodes:
674
+ node.cf_state = ControlFlowState(node.cf_state)
675
+ for node in references:
676
+ node.cf_state = ControlFlowState(node.cf_state)
677
+
678
+
679
+ class AssignmentCollector(TreeVisitor):
680
+ def __init__(self):
681
+ super().__init__()
682
+ self.assignments = []
683
+
684
+ def visit_Node(self):
685
+ self._visitchildren(self, None, None)
686
+
687
+ def visit_SingleAssignmentNode(self, node):
688
+ self.assignments.append((node.lhs, node.rhs))
689
+
690
+ def visit_CascadedAssignmentNode(self, node):
691
+ for lhs in node.lhs_list:
692
+ self.assignments.append((lhs, node.rhs))
693
+
694
+
695
+ class ControlFlowAnalysis(CythonTransform):
696
+
697
+ def find_in_stack(self, env):
698
+ if env == self.env:
699
+ return self.flow
700
+ for e, flow in reversed(self.stack):
701
+ if e is env:
702
+ return flow
703
+ assert False
704
+
705
+ def visit_ModuleNode(self, node):
706
+ dot_output = self.current_directives['control_flow.dot_output']
707
+ self.gv_ctx = GVContext() if dot_output else None
708
+
709
+ from .Optimize import ConstantFolding
710
+ self.constant_folder = ConstantFolding()
711
+
712
+ # Set of NameNode reductions
713
+ self.reductions = set()
714
+
715
+ self.in_inplace_assignment = False
716
+ self.env = node.scope
717
+ self.flow = ControlFlow()
718
+ self.stack = [] # a stack of (env, flow) tuples
719
+ self.object_expr = TypedExprNode(PyrexTypes.py_object_type, may_be_none=True)
720
+ self.visitchildren(node)
721
+
722
+ check_definitions(self.flow, self.current_directives)
723
+
724
+ if dot_output:
725
+ annotate_defs = self.current_directives['control_flow.dot_annotate_defs']
726
+ with open(dot_output, 'w') as fp:
727
+ self.gv_ctx.render(fp, 'module', annotate_defs=annotate_defs)
728
+ return node
729
+
730
+ def visit_FuncDefNode(self, node):
731
+ for arg in node.args:
732
+ if arg.default:
733
+ self.visitchildren(arg)
734
+ self.visitchildren(node, ('decorators',))
735
+ self.stack.append((self.env, self.flow))
736
+ self.env = node.local_scope
737
+ self.flow = ControlFlow()
738
+
739
+ # Collect all entries
740
+ for entry in node.local_scope.entries.values():
741
+ if self.flow.is_tracked(entry):
742
+ self.flow.entries.add(entry)
743
+
744
+ self.mark_position(node)
745
+ # Function body block
746
+ self.flow.nextblock()
747
+
748
+ for arg in node.args:
749
+ self._visit(arg)
750
+ if node.star_arg:
751
+ self.flow.mark_argument(node.star_arg,
752
+ TypedExprNode(Builtin.tuple_type,
753
+ may_be_none=False),
754
+ node.star_arg.entry)
755
+ if node.starstar_arg:
756
+ self.flow.mark_argument(node.starstar_arg,
757
+ TypedExprNode(Builtin.dict_type,
758
+ may_be_none=False),
759
+ node.starstar_arg.entry)
760
+ self._visit(node.body)
761
+ # Workaround for generators
762
+ if node.is_generator:
763
+ self._visit(node.gbody.body)
764
+
765
+ # Exit point
766
+ if self.flow.block:
767
+ self.flow.block.add_child(self.flow.exit_point)
768
+
769
+ # Cleanup graph
770
+ self.flow.normalize()
771
+ check_definitions(self.flow, self.current_directives)
772
+ self.flow.blocks.add(self.flow.entry_point)
773
+
774
+ if self.gv_ctx is not None:
775
+ self.gv_ctx.add(GV(node.local_scope.name, self.flow))
776
+
777
+ self.env, self.flow = self.stack.pop()
778
+ return node
779
+
780
+ def visit_DefNode(self, node):
781
+ node.used = True
782
+ return self.visit_FuncDefNode(node)
783
+
784
+ def visit_GeneratorBodyDefNode(self, node):
785
+ return node
786
+
787
+ def visit_CTypeDefNode(self, node):
788
+ return node
789
+
790
+ def mark_assignment(self, lhs, rhs=None, rhs_scope=None):
791
+ if not self.flow.block:
792
+ return
793
+ if self.flow.exceptions:
794
+ exc_descr = self.flow.exceptions[-1]
795
+ self.flow.block.add_child(exc_descr.entry_point)
796
+ self.flow.nextblock()
797
+
798
+ if not rhs:
799
+ rhs = self.object_expr
800
+ if lhs.is_name:
801
+ if lhs.entry is not None:
802
+ entry = lhs.entry
803
+ else:
804
+ entry = self.env.lookup(lhs.name)
805
+ if entry is None: # TODO: This shouldn't happen...
806
+ return
807
+ self.flow.mark_assignment(lhs, rhs, entry, rhs_scope=rhs_scope)
808
+ elif lhs.is_sequence_constructor:
809
+ for i, arg in enumerate(lhs.args):
810
+ if arg.is_starred:
811
+ # "a, *b = x" assigns a list to "b"
812
+ item_node = TypedExprNode(Builtin.list_type, may_be_none=False, pos=arg.pos)
813
+ elif rhs is self.object_expr:
814
+ item_node = rhs
815
+ else:
816
+ item_node = rhs.inferable_item_node(i)
817
+ self.mark_assignment(arg, item_node, rhs_scope=rhs_scope)
818
+ else:
819
+ self._visit(lhs)
820
+
821
+ if self.flow.exceptions:
822
+ exc_descr = self.flow.exceptions[-1]
823
+ self.flow.block.add_child(exc_descr.entry_point)
824
+ self.flow.nextblock()
825
+
826
+ def mark_position(self, node):
827
+ """Mark position if DOT output is enabled."""
828
+ if self.current_directives['control_flow.dot_output']:
829
+ self.flow.mark_position(node)
830
+
831
+ def visit_FromImportStatNode(self, node):
832
+ for name, target in node.items:
833
+ if name != "*":
834
+ self.mark_assignment(target)
835
+ self.visitchildren(node)
836
+ return node
837
+
838
+ def visit_AssignmentNode(self, node):
839
+ raise InternalError("Unhandled assignment node %s" % type(node))
840
+
841
+ def visit_SingleAssignmentNode(self, node):
842
+ self._visit(node.rhs)
843
+ self.mark_assignment(node.lhs, node.rhs)
844
+ return node
845
+
846
+ def visit_CascadedAssignmentNode(self, node):
847
+ self._visit(node.rhs)
848
+ for lhs in node.lhs_list:
849
+ self.mark_assignment(lhs, node.rhs)
850
+ return node
851
+
852
+ def visit_ParallelAssignmentNode(self, node):
853
+ collector = AssignmentCollector()
854
+ collector.visitchildren(node)
855
+ for lhs, rhs in collector.assignments:
856
+ self._visit(rhs)
857
+ for lhs, rhs in collector.assignments:
858
+ self.mark_assignment(lhs, rhs)
859
+ return node
860
+
861
+ def visit_InPlaceAssignmentNode(self, node):
862
+ self.in_inplace_assignment = True
863
+ self.visitchildren(node)
864
+ self.in_inplace_assignment = False
865
+ self.mark_assignment(node.lhs, self.constant_folder(node.create_binop_node()))
866
+ return node
867
+
868
+ def visit_DelStatNode(self, node):
869
+ for arg in node.args:
870
+ if arg.is_name:
871
+ entry = arg.entry or self.env.lookup(arg.name)
872
+ if entry.in_closure or entry.from_closure:
873
+ error(arg.pos,
874
+ "can not delete variable '%s' "
875
+ "referenced in nested scope" % entry.name)
876
+ if not node.ignore_nonexisting:
877
+ self._visit(arg) # mark reference
878
+ self.flow.mark_deletion(arg, entry)
879
+ else:
880
+ self._visit(arg)
881
+ return node
882
+
883
+ def visit_CArgDeclNode(self, node):
884
+ entry = self.env.lookup(node.name)
885
+ if entry:
886
+ may_be_none = not node.not_none
887
+ self.flow.mark_argument(
888
+ node, TypedExprNode(entry.type, may_be_none), entry)
889
+ return node
890
+
891
+ def visit_NameNode(self, node):
892
+ if self.flow.block:
893
+ entry = node.entry or self.env.lookup(node.name)
894
+ if entry:
895
+ self.flow.mark_reference(node, entry)
896
+
897
+ if entry in self.reductions and not self.in_inplace_assignment:
898
+ error(node.pos,
899
+ "Cannot read reduction variable in loop body")
900
+
901
+ return node
902
+
903
+ def visit_StatListNode(self, node):
904
+ if self.flow.block:
905
+ for stat in node.stats:
906
+ self._visit(stat)
907
+ if not self.flow.block:
908
+ stat.is_terminator = True
909
+ break
910
+ return node
911
+
912
+ def visit_Node(self, node):
913
+ self.visitchildren(node)
914
+ self.mark_position(node)
915
+ return node
916
+
917
+ def visit_SizeofVarNode(self, node):
918
+ return node
919
+
920
+ def visit_TypeidNode(self, node):
921
+ return node
922
+
923
+ def visit_IfStatNode(self, node):
924
+ next_block = self.flow.newblock()
925
+ parent = self.flow.block
926
+ # If clauses
927
+ for clause in node.if_clauses:
928
+ parent = self.flow.nextblock(parent)
929
+ self._visit(clause.condition)
930
+ self.flow.nextblock()
931
+ self._visit(clause.body)
932
+ if self.flow.block:
933
+ self.flow.block.add_child(next_block)
934
+ # Else clause
935
+ if node.else_clause:
936
+ self.flow.nextblock(parent=parent)
937
+ self._visit(node.else_clause)
938
+ if self.flow.block:
939
+ self.flow.block.add_child(next_block)
940
+ else:
941
+ parent.add_child(next_block)
942
+
943
+ if next_block.parents:
944
+ self.flow.block = next_block
945
+ else:
946
+ self.flow.block = None
947
+ return node
948
+
949
+ def visit_AssertStatNode(self, node):
950
+ """Essentially an if-condition that wraps a RaiseStatNode.
951
+ """
952
+ self.mark_position(node)
953
+ next_block = self.flow.newblock()
954
+ parent = self.flow.block
955
+ # failure case
956
+ parent = self.flow.nextblock(parent)
957
+ self._visit(node.condition)
958
+ self.flow.nextblock()
959
+ self._visit(node.exception)
960
+ if self.flow.block:
961
+ self.flow.block.add_child(next_block)
962
+ parent.add_child(next_block)
963
+ if next_block.parents:
964
+ self.flow.block = next_block
965
+ else:
966
+ self.flow.block = None
967
+ return node
968
+
969
+ def visit_WhileStatNode(self, node):
970
+ condition_block = self.flow.nextblock()
971
+ next_block = self.flow.newblock()
972
+ # Condition block
973
+ self.flow.loops.append(LoopDescr(next_block, condition_block))
974
+ if node.condition:
975
+ self._visit(node.condition)
976
+ # Body block
977
+ self.flow.nextblock()
978
+ self._visit(node.body)
979
+ self.flow.loops.pop()
980
+ # Loop it
981
+ if self.flow.block:
982
+ self.flow.block.add_child(condition_block)
983
+ self.flow.block.add_child(next_block)
984
+ # Else clause
985
+ if node.else_clause:
986
+ self.flow.nextblock(parent=condition_block)
987
+ self._visit(node.else_clause)
988
+ if self.flow.block:
989
+ self.flow.block.add_child(next_block)
990
+ else:
991
+ condition_block.add_child(next_block)
992
+
993
+ if next_block.parents:
994
+ self.flow.block = next_block
995
+ else:
996
+ self.flow.block = None
997
+ return node
998
+
999
+ def mark_forloop_target(self, node):
1000
+ # TODO: Remove redundancy with range optimization...
1001
+ is_special = False
1002
+ sequence = node.iterator.sequence
1003
+ target = node.target
1004
+ env = node.iterator.expr_scope or self.env
1005
+ if isinstance(sequence, ExprNodes.SimpleCallNode):
1006
+ function = sequence.function
1007
+ if sequence.self is None and function.is_name:
1008
+ entry = env.lookup(function.name)
1009
+ if not entry or entry.is_builtin:
1010
+ if function.name == 'reversed' and len(sequence.args) == 1:
1011
+ sequence = sequence.args[0]
1012
+ elif function.name == 'enumerate' and len(sequence.args) == 1:
1013
+ if target.is_sequence_constructor and len(target.args) == 2:
1014
+ iterator = sequence.args[0]
1015
+ if iterator.is_name:
1016
+ iterator_type = iterator.infer_type(env)
1017
+ if iterator_type.is_builtin_type:
1018
+ # assume that builtin types have a length within Py_ssize_t
1019
+ self.mark_assignment(
1020
+ target.args[0],
1021
+ ExprNodes.IntNode(target.pos, value='PY_SSIZE_T_MAX',
1022
+ type=PyrexTypes.c_py_ssize_t_type),
1023
+ rhs_scope=node.iterator.expr_scope)
1024
+ target = target.args[1]
1025
+ sequence = sequence.args[0]
1026
+ if isinstance(sequence, ExprNodes.SimpleCallNode):
1027
+ function = sequence.function
1028
+ if sequence.self is None and function.is_name and function.name in ('range', 'xrange'):
1029
+ entry = env.lookup(function.name)
1030
+ if entry and entry.is_type and entry.type is Builtin.range_type:
1031
+ is_special = True
1032
+ for arg in sequence.args[:2]:
1033
+ self.mark_assignment(target, arg, rhs_scope=node.iterator.expr_scope)
1034
+ if len(sequence.args) > 2:
1035
+ self.mark_assignment(target, self.constant_folder(
1036
+ ExprNodes.binop_node(node.pos,
1037
+ '+',
1038
+ sequence.args[0],
1039
+ sequence.args[2])),
1040
+ rhs_scope=node.iterator.expr_scope)
1041
+
1042
+ if not is_special:
1043
+ # A for-loop basically translates to subsequent calls to
1044
+ # __getitem__(), so using an IndexNode here allows us to
1045
+ # naturally infer the base type of pointers, C arrays,
1046
+ # Python strings, etc., while correctly falling back to an
1047
+ # object type when the base type cannot be handled.
1048
+
1049
+ self.mark_assignment(target, node.item, rhs_scope=node.iterator.expr_scope)
1050
+
1051
+ def mark_parallel_forloop_assignment(self, node):
1052
+ target = node.target
1053
+ for arg in node.args[:2]:
1054
+ self.mark_assignment(target, arg)
1055
+ if len(node.args) > 2:
1056
+ self.mark_assignment(target, self.constant_folder(
1057
+ ExprNodes.binop_node(node.pos,
1058
+ '+',
1059
+ node.args[0],
1060
+ node.args[2])))
1061
+ if not node.args:
1062
+ # Almost certainly an error
1063
+ self.mark_assignment(target)
1064
+
1065
+ def visit_AsyncForStatNode(self, node):
1066
+ return self.visit_ForInStatNode(node)
1067
+
1068
+ def visit_ForInStatNode(self, node):
1069
+ condition_block = self.flow.nextblock()
1070
+ next_block = self.flow.newblock()
1071
+ # Condition with iterator
1072
+ self.flow.loops.append(LoopDescr(next_block, condition_block))
1073
+ self._visit(node.iterator)
1074
+ # Target assignment
1075
+ self.flow.nextblock()
1076
+
1077
+ if isinstance(node, Nodes.ForInStatNode):
1078
+ self.mark_forloop_target(node)
1079
+ elif isinstance(node, Nodes.AsyncForStatNode):
1080
+ # not entirely correct, but good enough for now
1081
+ self.mark_assignment(node.target, node.item)
1082
+ elif isinstance(node, Nodes.ParallelRangeNode): # Parallel
1083
+ self.mark_parallel_forloop_assignment(node)
1084
+ else:
1085
+ assert False, type(node)
1086
+
1087
+ # Body block
1088
+
1089
+ self.flow.nextblock()
1090
+ self._visit(node.body)
1091
+ self.flow.loops.pop()
1092
+
1093
+ # Loop it
1094
+ if self.flow.block:
1095
+ self.flow.block.add_child(condition_block)
1096
+ # Else clause
1097
+ if node.else_clause:
1098
+ self.flow.nextblock(parent=condition_block)
1099
+ self._visit(node.else_clause)
1100
+ if self.flow.block:
1101
+ self.flow.block.add_child(next_block)
1102
+ else:
1103
+ condition_block.add_child(next_block)
1104
+
1105
+ if next_block.parents:
1106
+ self.flow.block = next_block
1107
+ else:
1108
+ self.flow.block = None
1109
+ return node
1110
+
1111
+ def _delete_privates(self, node, exclude=None):
1112
+ for private_node in node.assigned_nodes:
1113
+ if not exclude or private_node.entry is not exclude:
1114
+ self.flow.mark_deletion(private_node, private_node.entry)
1115
+
1116
+ def visit_ParallelRangeNode(self, node):
1117
+ reductions = self.reductions
1118
+
1119
+ # if node.target is None or not a NameNode, an error will have
1120
+ # been previously issued
1121
+ if hasattr(node.target, 'entry'):
1122
+ self.reductions = set(reductions)
1123
+
1124
+ for private_node in node.assigned_nodes:
1125
+ private_node.entry.error_on_uninitialized = True
1126
+ pos, reduction = node.assignments[private_node.entry]
1127
+ if reduction:
1128
+ self.reductions.add(private_node.entry)
1129
+
1130
+ node = self.visit_ForInStatNode(node)
1131
+
1132
+ self.reductions = reductions
1133
+ return node
1134
+
1135
+ def visit_ParallelWithBlockNode(self, node):
1136
+ for private_node in node.assigned_nodes:
1137
+ private_node.entry.error_on_uninitialized = True
1138
+
1139
+ self.visitchildren(node)
1140
+ # lastprivate isn't allowed/doesn't make sense for a parallel (non-for) block
1141
+ self._delete_privates(node)
1142
+
1143
+ return node
1144
+
1145
+ def visit_ForFromStatNode(self, node):
1146
+ condition_block = self.flow.nextblock()
1147
+ next_block = self.flow.newblock()
1148
+ # Condition with iterator
1149
+ self.flow.loops.append(LoopDescr(next_block, condition_block))
1150
+ self._visit(node.bound1)
1151
+ self._visit(node.bound2)
1152
+ if node.step is not None:
1153
+ self._visit(node.step)
1154
+ # Target assignment
1155
+ self.flow.nextblock()
1156
+ self.mark_assignment(node.target, node.bound1)
1157
+ if node.step is not None:
1158
+ self.mark_assignment(node.target, self.constant_folder(
1159
+ ExprNodes.binop_node(node.pos, '+', node.bound1, node.step)))
1160
+ # Body block
1161
+ self.flow.nextblock()
1162
+ self._visit(node.body)
1163
+ self.flow.loops.pop()
1164
+ # Loop it
1165
+ if self.flow.block:
1166
+ self.flow.block.add_child(condition_block)
1167
+ # Else clause
1168
+ if node.else_clause:
1169
+ self.flow.nextblock(parent=condition_block)
1170
+ self._visit(node.else_clause)
1171
+ if self.flow.block:
1172
+ self.flow.block.add_child(next_block)
1173
+ else:
1174
+ condition_block.add_child(next_block)
1175
+
1176
+ if next_block.parents:
1177
+ self.flow.block = next_block
1178
+ else:
1179
+ self.flow.block = None
1180
+ return node
1181
+
1182
+ def visit_LoopNode(self, node):
1183
+ raise InternalError("Generic loops are not supported")
1184
+
1185
+ def visit_WithTargetAssignmentStatNode(self, node):
1186
+ self.mark_assignment(node.lhs, node.with_node.enter_call)
1187
+ return node
1188
+
1189
+ def visit_WithStatNode(self, node):
1190
+ self._visit(node.manager)
1191
+ self._visit(node.enter_call)
1192
+ self._visit(node.body)
1193
+ return node
1194
+
1195
+ def visit_TryExceptStatNode(self, node):
1196
+ # After exception handling
1197
+ next_block = self.flow.newblock()
1198
+ # Body block
1199
+ self.flow.newblock()
1200
+ # Exception entry point
1201
+ entry_point = self.flow.newblock()
1202
+ self.flow.exceptions.append(ExceptionDescr(entry_point))
1203
+ self.flow.nextblock()
1204
+ ## XXX: links to exception handling point should be added by
1205
+ ## XXX: children nodes
1206
+ self.flow.block.add_child(entry_point)
1207
+ self.flow.nextblock()
1208
+ self.flow.in_try_block += 1
1209
+ self._visit(node.body)
1210
+ self.flow.in_try_block -= 1
1211
+ self.flow.exceptions.pop()
1212
+
1213
+ # After exception
1214
+ if self.flow.block:
1215
+ if node.else_clause:
1216
+ self.flow.nextblock()
1217
+ self._visit(node.else_clause)
1218
+ if self.flow.block:
1219
+ self.flow.block.add_child(next_block)
1220
+
1221
+ for clause in node.except_clauses:
1222
+ self.flow.block = entry_point
1223
+ if clause.pattern:
1224
+ for pattern in clause.pattern:
1225
+ self._visit(pattern)
1226
+ else:
1227
+ # TODO: handle * pattern
1228
+ pass
1229
+ entry_point = self.flow.newblock(parent=self.flow.block)
1230
+ self.flow.nextblock()
1231
+ if clause.target:
1232
+ self.mark_assignment(clause.target, clause.exc_value)
1233
+ self._visit(clause.body)
1234
+ if self.flow.block:
1235
+ self.flow.block.add_child(next_block)
1236
+
1237
+ if self.flow.exceptions:
1238
+ entry_point.add_child(self.flow.exceptions[-1].entry_point)
1239
+
1240
+ if next_block.parents:
1241
+ self.flow.block = next_block
1242
+ else:
1243
+ self.flow.block = None
1244
+ return node
1245
+
1246
+ def visit_TryFinallyStatNode(self, node):
1247
+ body_block = self.flow.nextblock()
1248
+
1249
+ # Exception entry point
1250
+ entry_point = self.flow.newblock()
1251
+ self.flow.block = entry_point
1252
+ self._visit(node.finally_except_clause)
1253
+
1254
+ if self.flow.block and self.flow.exceptions:
1255
+ self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1256
+
1257
+ # Normal execution
1258
+ finally_enter = self.flow.newblock()
1259
+ self.flow.block = finally_enter
1260
+ self._visit(node.finally_clause)
1261
+ finally_exit = self.flow.block
1262
+
1263
+ descr = ExceptionDescr(entry_point, finally_enter, finally_exit)
1264
+ self.flow.exceptions.append(descr)
1265
+ if self.flow.loops:
1266
+ self.flow.loops[-1].exceptions.append(descr)
1267
+ self.flow.block = body_block
1268
+ body_block.add_child(entry_point)
1269
+ self.flow.nextblock()
1270
+ self.flow.in_try_block += 1
1271
+ self._visit(node.body)
1272
+ self.flow.in_try_block -= 1
1273
+ self.flow.exceptions.pop()
1274
+ if self.flow.loops:
1275
+ self.flow.loops[-1].exceptions.pop()
1276
+
1277
+ if self.flow.block:
1278
+ self.flow.block.add_child(finally_enter)
1279
+ if finally_exit:
1280
+ self.flow.block = self.flow.nextblock(parent=finally_exit)
1281
+ else:
1282
+ self.flow.block = None
1283
+ return node
1284
+
1285
+ def visit_RaiseStatNode(self, node):
1286
+ self.mark_position(node)
1287
+ self.visitchildren(node)
1288
+ if self.flow.exceptions:
1289
+ self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1290
+ self.flow.block = None
1291
+ if self.flow.in_try_block:
1292
+ node.in_try_block = True
1293
+ return node
1294
+
1295
+ def visit_ReraiseStatNode(self, node):
1296
+ self.mark_position(node)
1297
+ if self.flow.exceptions:
1298
+ self.flow.block.add_child(self.flow.exceptions[-1].entry_point)
1299
+ self.flow.block = None
1300
+ return node
1301
+
1302
+ def visit_ReturnStatNode(self, node):
1303
+ self.mark_position(node)
1304
+ self.visitchildren(node)
1305
+
1306
+ outer_exception_handlers = iter(self.flow.exceptions[::-1])
1307
+ for handler in outer_exception_handlers:
1308
+ if handler.finally_enter:
1309
+ self.flow.block.add_child(handler.finally_enter)
1310
+ if handler.finally_exit:
1311
+ # 'return' goes to function exit, or to the next outer 'finally' clause
1312
+ exit_point = self.flow.exit_point
1313
+ for next_handler in outer_exception_handlers:
1314
+ if next_handler.finally_enter:
1315
+ exit_point = next_handler.finally_enter
1316
+ break
1317
+ handler.finally_exit.add_child(exit_point)
1318
+ break
1319
+ else:
1320
+ if self.flow.block:
1321
+ self.flow.block.add_child(self.flow.exit_point)
1322
+ self.flow.block = None
1323
+ return node
1324
+
1325
+ def visit_BreakStatNode(self, node):
1326
+ if not self.flow.loops:
1327
+ #error(node.pos, "break statement not inside loop")
1328
+ return node
1329
+ loop = self.flow.loops[-1]
1330
+ self.mark_position(node)
1331
+ for exception in loop.exceptions[::-1]:
1332
+ if exception.finally_enter:
1333
+ self.flow.block.add_child(exception.finally_enter)
1334
+ if exception.finally_exit:
1335
+ exception.finally_exit.add_child(loop.next_block)
1336
+ break
1337
+ else:
1338
+ self.flow.block.add_child(loop.next_block)
1339
+ self.flow.block = None
1340
+ return node
1341
+
1342
+ def visit_ContinueStatNode(self, node):
1343
+ if not self.flow.loops:
1344
+ #error(node.pos, "continue statement not inside loop")
1345
+ return node
1346
+ loop = self.flow.loops[-1]
1347
+ self.mark_position(node)
1348
+ for exception in loop.exceptions[::-1]:
1349
+ if exception.finally_enter:
1350
+ self.flow.block.add_child(exception.finally_enter)
1351
+ if exception.finally_exit:
1352
+ exception.finally_exit.add_child(loop.loop_block)
1353
+ break
1354
+ else:
1355
+ self.flow.block.add_child(loop.loop_block)
1356
+ self.flow.block = None
1357
+ return node
1358
+
1359
+ def visit_ComprehensionNode(self, node):
1360
+ if node.expr_scope:
1361
+ self.stack.append((self.env, self.flow))
1362
+ self.env = node.expr_scope
1363
+ # Skip append node here
1364
+ self._visit(node.loop)
1365
+ if node.expr_scope:
1366
+ self.env, _ = self.stack.pop()
1367
+ return node
1368
+
1369
+ def visit_ScopedExprNode(self, node):
1370
+ # currently this is written to deal with these two types
1371
+ # (with comprehensions covered in their own function)
1372
+ assert isinstance(node, (ExprNodes.IteratorNode, ExprNodes.AsyncIteratorNode)), node
1373
+ if node.expr_scope:
1374
+ self.stack.append((self.env, self.flow))
1375
+ self.flow = self.find_in_stack(node.expr_scope)
1376
+ self.env = node.expr_scope
1377
+ self.visitchildren(node)
1378
+ if node.expr_scope:
1379
+ self.env, self.flow = self.stack.pop()
1380
+ return node
1381
+
1382
+ def visit_PyClassDefNode(self, node):
1383
+ self.visitchildren(node, ('dict', 'metaclass',
1384
+ 'mkw', 'bases', 'class_result'))
1385
+ self.flow.mark_assignment(node.target, node.classobj,
1386
+ self.env.lookup(node.target.name))
1387
+ self.stack.append((self.env, self.flow))
1388
+ self.env = node.scope
1389
+ self.flow.nextblock()
1390
+ if node.doc_node:
1391
+ self.flow.mark_assignment(node.doc_node, fake_rhs_expr, node.doc_node.entry)
1392
+ self.visitchildren(node, ('body',))
1393
+ self.flow.nextblock()
1394
+ self.env, _ = self.stack.pop()
1395
+ return node
1396
+
1397
+ def visit_CClassDefNode(self, node):
1398
+ # just make sure the nodes scope is findable in-case there is a list comprehension in it
1399
+ self.stack.append((node.scope, self.flow))
1400
+ self.visitchildren(node)
1401
+ self.stack.pop()
1402
+ return node
1403
+
1404
+ def visit_AmpersandNode(self, node):
1405
+ if node.operand.is_name:
1406
+ # Fake assignment to silence warning
1407
+ self.mark_assignment(node.operand, fake_rhs_expr)
1408
+ self.visitchildren(node)
1409
+ return node
1410
+
1411
+ def visit_BoolBinopNode(self, node):
1412
+ # Note - I don't believe BoolBinopResultNode needs special handling beyond this
1413
+ assert len(node.subexprs) == 2 # operand1 and operand2 only
1414
+
1415
+ next_block = self.flow.newblock()
1416
+ parent = self.flow.block
1417
+
1418
+ self._visit(node.operand1)
1419
+
1420
+ self.flow.nextblock()
1421
+ self._visit(node.operand2)
1422
+ if self.flow.block:
1423
+ self.flow.block.add_child(next_block)
1424
+
1425
+ parent.add_child(next_block)
1426
+
1427
+ if next_block.parents:
1428
+ self.flow.block = next_block
1429
+ else:
1430
+ self.flow.block = None
1431
+ return node
1432
+
1433
+ def visit_CondExprNode(self, node):
1434
+ assert len(node.subexprs) == 3
1435
+ self._visit(node.test)
1436
+ parent = self.flow.block
1437
+ next_block = self.flow.newblock()
1438
+ self.flow.nextblock()
1439
+ self._visit(node.true_val)
1440
+ if self.flow.block:
1441
+ self.flow.block.add_child(next_block)
1442
+ self.flow.nextblock(parent=parent)
1443
+ self._visit(node.false_val)
1444
+ if self.flow.block:
1445
+ self.flow.block.add_child(next_block)
1446
+
1447
+ if next_block.parents:
1448
+ self.flow.block = next_block
1449
+ else:
1450
+ self.flow.block = None
1451
+ return node