More control flow work
authorRobert Bradshaw <robertwb@math.washington.edu>
Tue, 25 Mar 2008 23:26:22 +0000 (16:26 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Tue, 25 Mar 2008 23:26:22 +0000 (16:26 -0700)
Cython/Compiler/ControlFlow.py
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
Cython/Compiler/Options.py
Cython/Compiler/Symtab.py

index 7bce1de5182d388fd428f34f9aef971e2f7c88e0..25e81a56a0976dc346259738e7ba4e1186acf1e1 100644 (file)
@@ -37,19 +37,19 @@ class ControlFlow:
         self.parent.end_pos = pos
         return LinearControlFlow(pos, self.parent)
         
-    def get_state(self, pos, item):
-        return self.get_pos_state(pos, item)[1]
+    def get_state(self, item, pos=((),())):
+        return self.get_pos_state(item, pos)[1]
         
-    def get_pos_state(self, pos, item):
+    def get_pos_state(self, item, pos=((),())):
         # do some caching
         if pos > self.end_pos:
             try:
                 return self.tip[item]
             except KeyError:
-                self.tip[item] = pos_state = self._get_pos_state(pos, item)
+                self.tip[item] = pos_state = self._get_pos_state(item, pos)
                 return pos_state
         else:
-            return self._get_pos_state(pos, item)
+            return self._get_pos_state(item, pos)
         
 class LinearControlFlow(ControlFlow):
 
@@ -72,7 +72,7 @@ class LinearControlFlow(ControlFlow):
             bisect.insort(event_list, (pos, state))
             
         
-    def _get_pos_state(self, pos, item):
+    def _get_pos_state(self, item, pos):
         if pos > self.start_pos:
             if self.events.has_key(item):
                 event_list = self.events[item]
@@ -81,12 +81,12 @@ class LinearControlFlow(ControlFlow):
                         return event
                         
         if self.incoming is not None:
-            return self.incoming.get_pos_state(pos, item)
+            return self.incoming.get_pos_state(item, pos)
         
         else:
             return None, None
             
-    def to_string(self, indent, limit=None):
+    def to_string(self, indent='', limit=None):
     
         if len(self.events) == 0:
             s = indent + "[no state changes]"
@@ -124,19 +124,19 @@ class BranchingControlFlow(ControlFlow):
                     branch.set_state(pos, item, state)
                     return
     
-    def _get_pos_state(self, pos, item):
+    def _get_pos_state(self, item, pos):
         if pos <= self.start_pos:
-            return self.incoming.get_pos_state(pos, item)
+            return self.incoming.get_pos_state(item, pos)
         elif pos < self.end_pos:
             for branch_pos, branch in zip(self.branch_starts[::-1], self.branches[::-1]):
                 if pos >= branch_pos:
-                    return branch.get_pos_state(pos, item)
+                    return branch.get_pos_state(item, pos)
         else:
-            last_pos, last_state = self.branches[0].get_pos_state(pos, item)
+            last_pos, last_state = self.branches[0].get_pos_state(item, pos)
             if last_state is None:
                 return None, None
             for branch in self.branches[1:]:
-                other_pos, other_state = branch.get_pos_state(pos, item)
+                other_pos, other_state = branch.get_pos_state(item, pos)
                 if other_state is None or other_state != last_state:
                     return None, None
                 elif last_pos is not other_pos:
@@ -148,7 +148,7 @@ class BranchingControlFlow(ControlFlow):
         self.branch_starts.append(pos)
         return self.branches[-1]
 
-    def to_string(self, indent, limit=None):
+    def to_string(self, indent='', limit=None):
         join = "\n%sor\n" % indent
         s = join.join([branch.to_string(indent+"    ", limit=self.incoming) for branch in self.branches])
         if self.incoming is not limit and self.incoming is not None:
index a7b0a33cc2a68a1d3b04f0e565f2b152531b758e..b75178a8c98c2bfba6c783bd1d93820c01c5f0d5 100644 (file)
@@ -818,6 +818,7 @@ class NameNode(AtomicExprNode):
         if not self.entry:
             self.entry = env.declare_var(self.name, py_object_type, self.pos)
         env.control_flow.set_state(self.pos, (self.name, 'initalized'), True)
+        env.control_flow.set_state(self.pos, (self.name, 'source'), 'assignment')
         if self.entry.is_declared_generic:
             self.result_ctype = py_object_type
     
@@ -947,11 +948,12 @@ class NameNode(AtomicExprNode):
                     self.entry.name,
                     code.error_goto_if_null(self.result_code, self.pos)))
         elif entry.is_local:
-            assigned = entry.scope.control_flow.get_state(self.pos, (entry.name, 'initalized'))
+            assigned = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos)
             if assigned is False:
                 error(self.pos, "local variable '%s' referenced before assignment" % entry.name)
-            elif assigned is None:
-                code.putln('/* check %s */' % entry.cname)
+            elif not Options.init_local_none and assigned is None:
+                code.putln('if (%s == 0) { PyErr_SetString(PyExc_UnboundLocalError, "%s"); %s }' % (entry.cname, entry.name, code.error_goto(self.pos)))
+                entry.scope.control_flow.set_state(self.pos, (entry.name, 'initalized'), True)
 
     def generate_assignment_code(self, rhs, code):
         #print "NameNode.generate_assignment_code:", self.name ###
@@ -1004,7 +1006,14 @@ class NameNode(AtomicExprNode):
                 #print "...LHS type", self.type, "ctype", self.ctype() ###
                 #print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
                 rhs.make_owned_reference(code)
-                code.put_decref(self.result_code, self.ctype())
+                if entry.is_local and not Options.init_local_none:
+                    initalized = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos)
+                    if initalized is True:
+                        code.put_decref(self.result_code, self.ctype())
+                    elif initalized is None:
+                        code.put_xdecref(self.result_code, self.ctype())
+                else:
+                    code.put_decref(self.result_code, self.ctype())
             code.putln('%s = %s;' % (self.result_code, rhs.result_as(self.ctype())))
             if debug_disposal_code:
                 print("NameNode.generate_assignment_code:")
index 01a3284474cbc3375c9c134c2e5caa51e4b8bc7e..58cda726fce24f7de9e99685a13084eb3af5555d 100644 (file)
@@ -357,6 +357,7 @@ class CNameDeclaratorNode(CDeclaratorNode):
         self.entry = env.lookup(self.name)
         if self.rhs is not None:
             env.control_flow.set_state(self.rhs.end_pos(), (self.entry.name, 'initalized'), True)
+            env.control_flow.set_state(self.rhs.end_pos(), (self.entry.name, 'source'), 'assignment')
             self.entry.used = 1
             if self.type.is_pyobject:
                 self.entry.init_to_none = False
@@ -846,7 +847,11 @@ class FuncDefNode(StatNode, BlockNode):
             code.putln("PyGILState_STATE _save = PyGILState_Ensure();")
         # ----- Fetch arguments
         self.generate_argument_parsing_code(env, code)
-        self.generate_argument_increfs(lenv, code)
+        # If an argument is assigned to in the body, we must 
+        # incref it to properly keep track of refcounts.
+        for entry in lenv.arg_entries:
+            if entry.type.is_pyobject and lenv.control_flow.get_state((entry.name, 'source')) != 'arg':
+                code.put_var_incref(entry)
         # ----- Initialise local variables
         for entry in lenv.var_entries:
             if entry.type.is_pyobject and entry.init_to_none and entry.used:
@@ -899,9 +904,15 @@ class FuncDefNode(StatNode, BlockNode):
                             #self.return_type.default_value))
         # ----- Return cleanup
         code.put_label(code.return_label)
+        if not Options.init_local_none:
+            for entry in lenv.var_entries:
+                print entry.name, lenv.control_flow.get_state((entry.name, 'initalized'))
+                if lenv.control_flow.get_state((entry.name, 'initalized')) is not True:
+                    entry.xdecref_cleanup = 1
         code.put_var_decrefs(lenv.var_entries, used_only = 1)
+        # Decref any increfed args
         for entry in lenv.arg_entries:
-#            if len(entry.assignments) > 0:
+            if entry.type.is_pyobject and lenv.control_flow.get_state((entry.name, 'source')) != 'arg':
                 code.put_var_decref(entry)
         self.put_stararg_decrefs(code)
         if acquire_gil:
@@ -914,7 +925,7 @@ class FuncDefNode(StatNode, BlockNode):
         if self.py_func:
             self.py_func.generate_function_definitions(env, code, transforms)
         self.generate_optarg_wrapper_function(env, code)
-
+        
     def put_stararg_decrefs(self, code):
         pass
 
@@ -925,15 +936,6 @@ class FuncDefNode(StatNode, BlockNode):
             error(arg.pos,
                 "Argument type '%s' is incomplete" % arg.type)
         return env.declare_arg(arg.name, arg.type, arg.pos)
-    
-    def generate_argument_increfs(self, env, code):
-        # Turn borrowed argument refs into owned refs.
-        # This is necessary, because if the argument is
-        # assigned to, it will be decrefed.
-        for entry in env.arg_entries:
-#            if len(entry.assignments) > 0:
-                code.put_var_incref(entry)
-
     def generate_optarg_wrapper_function(self, env, code):
         pass
 
@@ -2535,9 +2537,6 @@ class ContinueStatNode(StatNode):
         elif not code.continue_label:
             error(self.pos, "continue statement not inside loop")
         else:
-            #code.putln(
-            #  "goto %s;" %
-            #          code.continue_label)
             code.put_goto(code.continue_label)
 
 
@@ -3144,13 +3143,18 @@ class TryExceptStatNode(StatNode):
     def analyse_control_flow(self, env):
         env.start_branching(self.pos)
         self.body.analyse_control_flow(env)
+        successful_try = env.control_flow # grab this for later
+        env.next_branch(self.body.end_pos())
+        env.finish_branching(self.body.end_pos())
+        
+        env.start_branching(self.except_clauses[0].pos)
         for except_clause in self.except_clauses:
-            env.next_branch(except_clause.pos)
             except_clause.analyse_control_flow(env)
+            env.next_branch(except_clause.end_pos())
+            
+        # the else cause it executed only when the try clause finishes
+        env.control_flow.incoming = successful_try
         if self.else_clause:
-            env.next_branch(self.except_clauses[-1].end_pos())
-            # the else cause it executed only when the try clause finishes
-            env.control_flow.incoming = env.control_flow.parent.branches[0]
             self.else_clause.analyse_control_flow(env)
         env.finish_branching(self.end_pos())
 
@@ -3320,8 +3324,8 @@ class TryFinallyStatNode(StatNode):
         env.start_branching(self.pos)
         self.body.analyse_control_flow(env)
         env.next_branch(self.body.end_pos())
+        env.finish_branching(self.body.end_pos())
         self.finally_clause.analyse_control_flow(env)
-        env.finish_branching(self.end_pos())
 
     def analyse_declarations(self, env):
         self.body.analyse_declarations(env)
index d7cc0974add5b66f30823e2ac141a9245204373f..03852327bad231c2ab8930d32be8e05fffa97b14 100644 (file)
@@ -39,3 +39,9 @@ convert_range = 0
 # lookup on every call. 
 # If this is 0 it simply creates a wrapper. 
 lookup_module_cpdef = 0
+
+# This will set local variables to None rather than NULL which may cause 
+# surpress what would be an UnboundLocalError in pure Python but eliminates 
+# checking for NULL on every use, and can decref rather than xdecref at the end. 
+# WARNING: This is a work in progress, may currently segfault.
+init_local_none = 1
index 955d4459baf8b3516f64c4ffb9465fb66e1ff53b..f414830faf8dd0cc233dda12e8c2d3be3ce57515 100644 (file)
@@ -1033,6 +1033,7 @@ class LocalScope(Scope):
         entry.is_arg = 1
         #entry.borrowed = 1 # Not using borrowed arg refs for now
         self.arg_entries.append(entry)
+        self.control_flow.set_state((), (name, 'source'), 'arg')
         return entry
     
     def declare_var(self, name, type, pos, 
@@ -1042,7 +1043,9 @@ class LocalScope(Scope):
             error(pos, "Local variable cannot be declared %s" % visibility)
         entry = Scope.declare_var(self, name, type, pos, 
             cname, visibility, is_cdef)
-        entry.init_to_none = type.is_pyobject
+        if type.is_pyobject and not Options.init_local_none:
+            entry.init = "0"
+        entry.init_to_none = type.is_pyobject and Options.init_local_none
         entry.is_local = 1
         self.var_entries.append(entry)
         return entry