enable control flow analysis for stack allocated structured variables (struct, union...
authorStefan Behnel <stefan_ml@behnel.de>
Sun, 27 Jan 2013 10:22:21 +0000 (11:22 +0100)
committerStefan Behnel <stefan_ml@behnel.de>
Sun, 27 Jan 2013 10:22:21 +0000 (11:22 +0100)
Cython/Compiler/FlowControl.py
tests/run/control_flow_stack_allocation.pyx [new file with mode: 0644]

index 119a2ee..f82bf98 100644 (file)
@@ -16,7 +16,8 @@ from Errors import error, warning, InternalError
 
 class TypedExprNode(ExprNodes.ExprNode):
     # Used for declaring assignments of a specified type without a known entry.
-    def __init__(self, type, may_be_none=None):
+    def __init__(self, type, may_be_none=None, pos=None):
+        self.pos = pos
         self.type = type
         self._may_be_none = may_be_none
 
@@ -147,13 +148,19 @@ class ControlFlow(object):
     def is_tracked(self, entry):
         if entry.is_anonymous:
             return False
-        if (entry.type.is_array or entry.type.is_struct_or_union or
-                entry.type.is_cpp_class):
-            return False
         return (entry.is_local or entry.is_pyclass_attr or entry.is_arg or
                 entry.from_closure or entry.in_closure or
                 entry.error_on_uninitialized)
 
+    def is_statically_assigned(self, entry):
+        if (entry.is_local and entry.is_variable and
+                (entry.type.is_struct_or_union or
+                 entry.type.is_array or
+                 entry.type.is_cpp_class)):
+            # stack allocated structured variable => never uninitialised
+            return True
+        return False
+
     def mark_position(self, node):
         """Mark position, will be used to draw graph nodes."""
         if self.block:
@@ -252,7 +259,9 @@ class ControlFlow(object):
         ret = set()
         assmts = self.assmts[entry]
         if istate & assmts.bit:
-            if entry.from_closure:
+            if self.is_statically_assigned(entry):
+                ret.add(StaticAssignment(entry))
+            elif entry.from_closure:
                 ret.add(Unknown)
             else:
                 ret.add(Uninitialized)
@@ -320,6 +329,24 @@ class NameAssignment(object):
         return self.rhs.type_dependencies(scope)
 
 
+class StaticAssignment(NameAssignment):
+    """Initialised at declaration time, e.g. stack allocation."""
+    def __init__(self, entry):
+        if not entry.type.is_pyobject:
+            may_be_none = False
+        else:
+            may_be_none = None  # unknown
+        lhs = TypedExprNode(
+            entry.type, may_be_none=may_be_none, pos=entry.pos)
+        super(StaticAssignment, self).__init__(lhs, lhs, entry)
+
+    def infer_type(self, scope):
+        return self.entry.type
+
+    def type_dependencies(self, scope):
+        return []
+
+
 class Argument(NameAssignment):
     def __init__(self, lhs, rhs, entry):
         NameAssignment.__init__(self, lhs, rhs, entry)
diff --git a/tests/run/control_flow_stack_allocation.pyx b/tests/run/control_flow_stack_allocation.pyx
new file mode 100644 (file)
index 0000000..3dcfee5
--- /dev/null
@@ -0,0 +1,33 @@
+# mode: run
+# tag: werror, control-flow
+# cython: warn.unused=True, warn.unused_arg=True, warn.unused_result=True
+
+cdef struct S:
+    int x
+    float y
+
+
+cdef stack_alloc_test(int[2] array_arg, S struct_arg):
+    cdef int[2] array_var
+    cdef S struct_var, struct_var_by_value
+
+    for i in range(2):
+        array_var[i] = array_arg[i]
+    struct_var.x, struct_var.y = struct_arg.x, struct_arg.y
+    struct_var_by_value = struct_var
+
+    return [ i for i in array_var ], struct_var_by_value
+
+
+def test():
+    """
+    >>> test()
+    ([0, 1], {'y': 2.0, 'x': 1})
+    """
+    cdef int[2] array_var
+    cdef S struct_var
+    for i in range(2):
+        array_var[i] = i
+    struct_var = [1, 2.0]
+
+    return stack_alloc_test(array_var, struct_var)