From: Robert Bradshaw Date: Tue, 25 Mar 2008 23:26:22 +0000 (-0700) Subject: More control flow work X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=6a8e0799a44d2bcde1353e07f6dc5d2b5c9a906a;p=cython.git More control flow work --- diff --git a/Cython/Compiler/ControlFlow.py b/Cython/Compiler/ControlFlow.py index 7bce1de5..25e81a56 100644 --- a/Cython/Compiler/ControlFlow.py +++ b/Cython/Compiler/ControlFlow.py @@ -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: diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index a7b0a33c..b75178a8 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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:") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 01a32844..58cda726 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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) diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py index d7cc0974..03852327 100644 --- a/Cython/Compiler/Options.py +++ b/Cython/Compiler/Options.py @@ -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 diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 955d4459..f414830f 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -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