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