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.
- Cython/Build/BuildExecutable.py +169 -0
- Cython/Build/Cache.py +199 -0
- Cython/Build/Cythonize.py +350 -0
- Cython/Build/Dependencies.py +1314 -0
- Cython/Build/Distutils.py +1 -0
- Cython/Build/Inline.py +463 -0
- Cython/Build/IpythonMagic.py +560 -0
- Cython/Build/SharedModule.py +94 -0
- Cython/Build/Tests/TestCyCache.py +194 -0
- Cython/Build/Tests/TestCythonizeArgsParser.py +481 -0
- Cython/Build/Tests/TestDependencies.py +133 -0
- Cython/Build/Tests/TestInline.py +177 -0
- Cython/Build/Tests/TestIpythonMagic.py +287 -0
- Cython/Build/Tests/TestRecythonize.py +212 -0
- Cython/Build/Tests/TestStripLiterals.py +155 -0
- Cython/Build/Tests/__init__.py +1 -0
- Cython/Build/__init__.py +11 -0
- Cython/CodeWriter.py +815 -0
- Cython/Compiler/AnalysedTreeTransforms.py +97 -0
- Cython/Compiler/Annotate.py +328 -0
- Cython/Compiler/AutoDocTransforms.py +320 -0
- Cython/Compiler/Buffer.py +680 -0
- Cython/Compiler/Builtin.py +984 -0
- Cython/Compiler/CmdLine.py +263 -0
- Cython/Compiler/Code.pxd +149 -0
- Cython/Compiler/Code.py +3746 -0
- Cython/Compiler/Code.pyd +0 -0
- Cython/Compiler/CodeGeneration.py +33 -0
- Cython/Compiler/CythonScope.py +191 -0
- Cython/Compiler/Dataclass.py +864 -0
- Cython/Compiler/DebugFlags.py +24 -0
- Cython/Compiler/Errors.py +297 -0
- Cython/Compiler/ExprNodes.py +15562 -0
- Cython/Compiler/FlowControl.pxd +97 -0
- Cython/Compiler/FlowControl.py +1451 -0
- Cython/Compiler/FlowControl.pyd +0 -0
- Cython/Compiler/FusedNode.py +971 -0
- Cython/Compiler/FusedNode.pyd +0 -0
- Cython/Compiler/Future.py +16 -0
- Cython/Compiler/Interpreter.py +57 -0
- Cython/Compiler/Lexicon.py +421 -0
- Cython/Compiler/LineTable.py +114 -0
- Cython/Compiler/LineTable.pyd +0 -0
- Cython/Compiler/Main.py +857 -0
- Cython/Compiler/MatchCaseNodes.py +259 -0
- Cython/Compiler/MemoryView.py +905 -0
- Cython/Compiler/ModuleNode.py +4235 -0
- Cython/Compiler/Naming.py +363 -0
- Cython/Compiler/Nodes.py +10831 -0
- Cython/Compiler/Optimize.py +5288 -0
- Cython/Compiler/Options.py +843 -0
- Cython/Compiler/ParseTreeTransforms.pxd +78 -0
- Cython/Compiler/ParseTreeTransforms.py +4638 -0
- Cython/Compiler/Parsing.pxd +9 -0
- Cython/Compiler/Parsing.py +4775 -0
- Cython/Compiler/Parsing.pyd +0 -0
- Cython/Compiler/Pipeline.py +439 -0
- Cython/Compiler/PyrexTypes.py +5870 -0
- Cython/Compiler/Pythran.py +232 -0
- Cython/Compiler/Scanning.pxd +48 -0
- Cython/Compiler/Scanning.py +701 -0
- Cython/Compiler/Scanning.pyd +0 -0
- Cython/Compiler/StringEncoding.py +298 -0
- Cython/Compiler/Symtab.py +3073 -0
- Cython/Compiler/Tests/TestBuffer.py +105 -0
- Cython/Compiler/Tests/TestBuiltin.py +72 -0
- Cython/Compiler/Tests/TestCmdLine.py +586 -0
- Cython/Compiler/Tests/TestCode.py +144 -0
- Cython/Compiler/Tests/TestFlowControl.py +65 -0
- Cython/Compiler/Tests/TestGrammar.py +202 -0
- Cython/Compiler/Tests/TestMemView.py +71 -0
- Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
- Cython/Compiler/Tests/TestScanning.py +134 -0
- Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
- Cython/Compiler/Tests/TestStringEncoding.py +21 -0
- Cython/Compiler/Tests/TestTreeFragment.py +63 -0
- Cython/Compiler/Tests/TestTreePath.py +103 -0
- Cython/Compiler/Tests/TestTypes.py +75 -0
- Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
- Cython/Compiler/Tests/TestVisitor.py +61 -0
- Cython/Compiler/Tests/Utils.py +36 -0
- Cython/Compiler/Tests/__init__.py +1 -0
- Cython/Compiler/TreeFragment.py +278 -0
- Cython/Compiler/TreePath.py +303 -0
- Cython/Compiler/TypeInference.py +591 -0
- Cython/Compiler/TypeSlots.py +1174 -0
- Cython/Compiler/UFuncs.py +311 -0
- Cython/Compiler/UtilNodes.py +389 -0
- Cython/Compiler/UtilityCode.py +344 -0
- Cython/Compiler/Version.py +8 -0
- Cython/Compiler/Visitor.pxd +53 -0
- Cython/Compiler/Visitor.py +861 -0
- Cython/Compiler/Visitor.pyd +0 -0
- Cython/Compiler/__init__.py +1 -0
- Cython/Coverage.py +448 -0
- Cython/Debugger/Cygdb.py +177 -0
- Cython/Debugger/DebugWriter.py +82 -0
- Cython/Debugger/Tests/TestLibCython.py +275 -0
- Cython/Debugger/Tests/__init__.py +1 -0
- Cython/Debugger/Tests/cfuncs.c +8 -0
- Cython/Debugger/Tests/codefile +49 -0
- Cython/Debugger/Tests/test_libcython_in_gdb.py +578 -0
- Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
- Cython/Debugger/__init__.py +1 -0
- Cython/Debugger/libcython.py +1548 -0
- Cython/Debugger/libpython.py +2821 -0
- Cython/Debugging.py +20 -0
- Cython/Distutils/__init__.py +2 -0
- Cython/Distutils/build_ext.py +139 -0
- Cython/Distutils/extension.py +96 -0
- Cython/Distutils/old_build_ext.py +351 -0
- Cython/Includes/cpython/__init__.pxd +173 -0
- Cython/Includes/cpython/array.pxd +178 -0
- Cython/Includes/cpython/bool.pxd +37 -0
- Cython/Includes/cpython/buffer.pxd +112 -0
- Cython/Includes/cpython/bytearray.pxd +33 -0
- Cython/Includes/cpython/bytes.pxd +200 -0
- Cython/Includes/cpython/cellobject.pxd +35 -0
- Cython/Includes/cpython/ceval.pxd +8 -0
- Cython/Includes/cpython/codecs.pxd +121 -0
- Cython/Includes/cpython/complex.pxd +60 -0
- Cython/Includes/cpython/contextvars.pxd +145 -0
- Cython/Includes/cpython/conversion.pxd +36 -0
- Cython/Includes/cpython/datetime.pxd +395 -0
- Cython/Includes/cpython/descr.pxd +26 -0
- Cython/Includes/cpython/dict.pxd +187 -0
- Cython/Includes/cpython/exc.pxd +263 -0
- Cython/Includes/cpython/fileobject.pxd +57 -0
- Cython/Includes/cpython/float.pxd +47 -0
- Cython/Includes/cpython/function.pxd +65 -0
- Cython/Includes/cpython/genobject.pxd +25 -0
- Cython/Includes/cpython/getargs.pxd +12 -0
- Cython/Includes/cpython/instance.pxd +25 -0
- Cython/Includes/cpython/iterator.pxd +36 -0
- Cython/Includes/cpython/iterobject.pxd +24 -0
- Cython/Includes/cpython/list.pxd +92 -0
- Cython/Includes/cpython/long.pxd +149 -0
- Cython/Includes/cpython/longintrepr.pxd +14 -0
- Cython/Includes/cpython/mapping.pxd +63 -0
- Cython/Includes/cpython/marshal.pxd +66 -0
- Cython/Includes/cpython/mem.pxd +120 -0
- Cython/Includes/cpython/memoryview.pxd +50 -0
- Cython/Includes/cpython/method.pxd +49 -0
- Cython/Includes/cpython/module.pxd +208 -0
- Cython/Includes/cpython/number.pxd +258 -0
- Cython/Includes/cpython/object.pxd +433 -0
- Cython/Includes/cpython/pycapsule.pxd +143 -0
- Cython/Includes/cpython/pylifecycle.pxd +68 -0
- Cython/Includes/cpython/pyport.pxd +8 -0
- Cython/Includes/cpython/pystate.pxd +95 -0
- Cython/Includes/cpython/pythread.pxd +53 -0
- Cython/Includes/cpython/ref.pxd +141 -0
- Cython/Includes/cpython/sequence.pxd +134 -0
- Cython/Includes/cpython/set.pxd +119 -0
- Cython/Includes/cpython/slice.pxd +70 -0
- Cython/Includes/cpython/time.pxd +129 -0
- Cython/Includes/cpython/tuple.pxd +72 -0
- Cython/Includes/cpython/type.pxd +53 -0
- Cython/Includes/cpython/unicode.pxd +639 -0
- Cython/Includes/cpython/version.pxd +32 -0
- Cython/Includes/cpython/weakref.pxd +78 -0
- Cython/Includes/libc/__init__.pxd +1 -0
- Cython/Includes/libc/complex.pxd +35 -0
- Cython/Includes/libc/errno.pxd +127 -0
- Cython/Includes/libc/float.pxd +43 -0
- Cython/Includes/libc/limits.pxd +28 -0
- Cython/Includes/libc/locale.pxd +46 -0
- Cython/Includes/libc/math.pxd +209 -0
- Cython/Includes/libc/setjmp.pxd +10 -0
- Cython/Includes/libc/signal.pxd +64 -0
- Cython/Includes/libc/stddef.pxd +9 -0
- Cython/Includes/libc/stdint.pxd +105 -0
- Cython/Includes/libc/stdio.pxd +80 -0
- Cython/Includes/libc/stdlib.pxd +72 -0
- Cython/Includes/libc/string.pxd +50 -0
- Cython/Includes/libc/threads.pxd +234 -0
- Cython/Includes/libc/time.pxd +52 -0
- Cython/Includes/libcpp/__init__.pxd +4 -0
- Cython/Includes/libcpp/algorithm.pxd +320 -0
- Cython/Includes/libcpp/any.pxd +16 -0
- Cython/Includes/libcpp/atomic.pxd +59 -0
- Cython/Includes/libcpp/barrier.pxd +22 -0
- Cython/Includes/libcpp/bit.pxd +29 -0
- Cython/Includes/libcpp/cast.pxd +12 -0
- Cython/Includes/libcpp/cmath.pxd +518 -0
- Cython/Includes/libcpp/complex.pxd +106 -0
- Cython/Includes/libcpp/condition_variable.pxd +322 -0
- Cython/Includes/libcpp/deque.pxd +165 -0
- Cython/Includes/libcpp/exception.pxd +86 -0
- Cython/Includes/libcpp/execution.pxd +15 -0
- Cython/Includes/libcpp/forward_list.pxd +63 -0
- Cython/Includes/libcpp/functional.pxd +26 -0
- Cython/Includes/libcpp/future.pxd +103 -0
- Cython/Includes/libcpp/iterator.pxd +34 -0
- Cython/Includes/libcpp/latch.pxd +17 -0
- Cython/Includes/libcpp/limits.pxd +61 -0
- Cython/Includes/libcpp/list.pxd +117 -0
- Cython/Includes/libcpp/map.pxd +252 -0
- Cython/Includes/libcpp/memory.pxd +115 -0
- Cython/Includes/libcpp/mutex.pxd +387 -0
- Cython/Includes/libcpp/numbers.pxd +15 -0
- Cython/Includes/libcpp/numeric.pxd +131 -0
- Cython/Includes/libcpp/optional.pxd +34 -0
- Cython/Includes/libcpp/pair.pxd +1 -0
- Cython/Includes/libcpp/queue.pxd +25 -0
- Cython/Includes/libcpp/random.pxd +166 -0
- Cython/Includes/libcpp/semaphore.pxd +43 -0
- Cython/Includes/libcpp/set.pxd +228 -0
- Cython/Includes/libcpp/shared_mutex.pxd +96 -0
- Cython/Includes/libcpp/span.pxd +87 -0
- Cython/Includes/libcpp/stack.pxd +11 -0
- Cython/Includes/libcpp/stop_token.pxd +117 -0
- Cython/Includes/libcpp/string.pxd +355 -0
- Cython/Includes/libcpp/string_view.pxd +183 -0
- Cython/Includes/libcpp/typeindex.pxd +15 -0
- Cython/Includes/libcpp/typeinfo.pxd +10 -0
- Cython/Includes/libcpp/unordered_map.pxd +193 -0
- Cython/Includes/libcpp/unordered_set.pxd +152 -0
- Cython/Includes/libcpp/utility.pxd +30 -0
- Cython/Includes/libcpp/vector.pxd +186 -0
- Cython/Includes/numpy/math.pxd +150 -0
- Cython/Includes/openmp.pxd +50 -0
- Cython/Includes/posix/__init__.pxd +1 -0
- Cython/Includes/posix/dlfcn.pxd +14 -0
- Cython/Includes/posix/fcntl.pxd +86 -0
- Cython/Includes/posix/ioctl.pxd +4 -0
- Cython/Includes/posix/mman.pxd +101 -0
- Cython/Includes/posix/resource.pxd +57 -0
- Cython/Includes/posix/select.pxd +21 -0
- Cython/Includes/posix/signal.pxd +73 -0
- Cython/Includes/posix/stat.pxd +98 -0
- Cython/Includes/posix/stdio.pxd +37 -0
- Cython/Includes/posix/stdlib.pxd +29 -0
- Cython/Includes/posix/strings.pxd +9 -0
- Cython/Includes/posix/time.pxd +71 -0
- Cython/Includes/posix/types.pxd +30 -0
- Cython/Includes/posix/uio.pxd +26 -0
- Cython/Includes/posix/unistd.pxd +271 -0
- Cython/Includes/posix/wait.pxd +38 -0
- Cython/Plex/Actions.pxd +24 -0
- Cython/Plex/Actions.py +119 -0
- Cython/Plex/Actions.pyd +0 -0
- Cython/Plex/DFA.pxd +14 -0
- Cython/Plex/DFA.py +164 -0
- Cython/Plex/DFA.pyd +0 -0
- Cython/Plex/Errors.py +48 -0
- Cython/Plex/Lexicons.py +178 -0
- Cython/Plex/Machines.pxd +36 -0
- Cython/Plex/Machines.py +238 -0
- Cython/Plex/Machines.pyd +0 -0
- Cython/Plex/Regexps.py +535 -0
- Cython/Plex/Scanners.pxd +45 -0
- Cython/Plex/Scanners.py +328 -0
- Cython/Plex/Scanners.pyd +0 -0
- Cython/Plex/Transitions.pxd +14 -0
- Cython/Plex/Transitions.py +239 -0
- Cython/Plex/Transitions.pyd +0 -0
- Cython/Plex/__init__.py +34 -0
- Cython/Runtime/__init__.py +1 -0
- Cython/Runtime/refnanny.pyd +0 -0
- Cython/Runtime/refnanny.pyx +237 -0
- Cython/Shadow.py +690 -0
- Cython/Shadow.pyi +521 -0
- Cython/StringIOTree.py +170 -0
- Cython/StringIOTree.pyd +0 -0
- Cython/Tempita/__init__.py +4 -0
- Cython/Tempita/_looper.py +154 -0
- Cython/Tempita/_tempita.py +1091 -0
- Cython/Tempita/_tempita.pyd +0 -0
- Cython/TestUtils.py +422 -0
- Cython/Tests/TestCodeWriter.py +128 -0
- Cython/Tests/TestCythonUtils.py +202 -0
- Cython/Tests/TestJediTyper.py +223 -0
- Cython/Tests/TestShadow.py +114 -0
- Cython/Tests/TestStringIOTree.py +67 -0
- Cython/Tests/TestTestUtils.py +90 -0
- Cython/Tests/__init__.py +1 -0
- Cython/Tests/xmlrunner.py +390 -0
- Cython/Utility/AsyncGen.c +1031 -0
- Cython/Utility/Buffer.c +865 -0
- Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
- Cython/Utility/Builtins.c +810 -0
- Cython/Utility/CConvert.pyx +134 -0
- Cython/Utility/CMath.c +104 -0
- Cython/Utility/CommonStructures.c +226 -0
- Cython/Utility/Complex.c +378 -0
- Cython/Utility/Coroutine.c +2300 -0
- Cython/Utility/CpdefEnums.pyx +103 -0
- Cython/Utility/CppConvert.pyx +282 -0
- Cython/Utility/CppSupport.cpp +151 -0
- Cython/Utility/CythonFunction.c +1832 -0
- Cython/Utility/Dataclasses.c +101 -0
- Cython/Utility/Embed.c +121 -0
- Cython/Utility/Exceptions.c +1016 -0
- Cython/Utility/ExtensionTypes.c +996 -0
- Cython/Utility/FunctionArguments.c +1043 -0
- Cython/Utility/FusedFunction.pyx +44 -0
- Cython/Utility/ImportExport.c +907 -0
- Cython/Utility/MemoryView.pxd +188 -0
- Cython/Utility/MemoryView.pyx +1482 -0
- Cython/Utility/MemoryView_C.c +927 -0
- Cython/Utility/ModuleSetupCode.c +3203 -0
- Cython/Utility/NumpyImportArray.c +46 -0
- Cython/Utility/ObjectHandling.c +3273 -0
- Cython/Utility/Optimize.c +1603 -0
- Cython/Utility/Overflow.c +384 -0
- Cython/Utility/Printing.c +86 -0
- Cython/Utility/Profile.c +732 -0
- Cython/Utility/StringTools.c +1379 -0
- Cython/Utility/Synchronization.c +399 -0
- Cython/Utility/TString.c +356 -0
- Cython/Utility/TestCyUtilityLoader.pyx +8 -0
- Cython/Utility/TestCythonScope.pyx +75 -0
- Cython/Utility/TestUtilityLoader.c +12 -0
- Cython/Utility/TypeConversion.c +1385 -0
- Cython/Utility/UFuncs.pyx +50 -0
- Cython/Utility/UFuncs_C.c +89 -0
- Cython/Utility/__init__.py +28 -0
- Cython/Utility/arrayarray.h +167 -0
- Cython/Utils.py +687 -0
- Cython/Utils.pyd +0 -0
- Cython/__init__.py +10 -0
- Cython/__init__.pyi +7 -0
- Cython/py.typed +0 -0
- cython-3.2.0.dist-info/METADATA +85 -0
- cython-3.2.0.dist-info/RECORD +333 -0
- cython-3.2.0.dist-info/WHEEL +5 -0
- cython-3.2.0.dist-info/entry_points.txt +4 -0
- cython-3.2.0.dist-info/top_level.txt +3 -0
- cython.py +29 -0
- pyximport/__init__.py +4 -0
- pyximport/pyxbuild.py +160 -0
- 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
|