From 01321f7bddb8e86a0e969f5afb199a466e17c46a Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sun, 27 Jan 2013 11:22:21 +0100 Subject: [PATCH] enable control flow analysis for stack allocated structured variables (struct, union, array, C++ class) --- Cython/Compiler/FlowControl.py | 37 +++++++++++++++++++++++++---- tests/run/control_flow_stack_allocation.pyx | 33 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 tests/run/control_flow_stack_allocation.pyx diff --git a/Cython/Compiler/FlowControl.py b/Cython/Compiler/FlowControl.py index 119a2ee..f82bf98 100644 --- a/Cython/Compiler/FlowControl.py +++ b/Cython/Compiler/FlowControl.py @@ -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 index 0000000..3dcfee5 --- /dev/null +++ b/tests/run/control_flow_stack_allocation.pyx @@ -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) -- 2.7.4