Commit 6a8e0799 authored by Robert Bradshaw's avatar Robert Bradshaw

More control flow work

parent 670cda8e
...@@ -37,19 +37,19 @@ class ControlFlow: ...@@ -37,19 +37,19 @@ class ControlFlow:
self.parent.end_pos = pos self.parent.end_pos = pos
return LinearControlFlow(pos, self.parent) return LinearControlFlow(pos, self.parent)
def get_state(self, pos, item): def get_state(self, item, pos=((),())):
return self.get_pos_state(pos, item)[1] 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 # do some caching
if pos > self.end_pos: if pos > self.end_pos:
try: try:
return self.tip[item] return self.tip[item]
except KeyError: 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 return pos_state
else: else:
return self._get_pos_state(pos, item) return self._get_pos_state(item, pos)
class LinearControlFlow(ControlFlow): class LinearControlFlow(ControlFlow):
...@@ -72,7 +72,7 @@ class LinearControlFlow(ControlFlow): ...@@ -72,7 +72,7 @@ class LinearControlFlow(ControlFlow):
bisect.insort(event_list, (pos, state)) 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 pos > self.start_pos:
if self.events.has_key(item): if self.events.has_key(item):
event_list = self.events[item] event_list = self.events[item]
...@@ -81,12 +81,12 @@ class LinearControlFlow(ControlFlow): ...@@ -81,12 +81,12 @@ class LinearControlFlow(ControlFlow):
return event return event
if self.incoming is not None: if self.incoming is not None:
return self.incoming.get_pos_state(pos, item) return self.incoming.get_pos_state(item, pos)
else: else:
return None, None return None, None
def to_string(self, indent, limit=None): def to_string(self, indent='', limit=None):
if len(self.events) == 0: if len(self.events) == 0:
s = indent + "[no state changes]" s = indent + "[no state changes]"
...@@ -124,19 +124,19 @@ class BranchingControlFlow(ControlFlow): ...@@ -124,19 +124,19 @@ class BranchingControlFlow(ControlFlow):
branch.set_state(pos, item, state) branch.set_state(pos, item, state)
return return
def _get_pos_state(self, pos, item): def _get_pos_state(self, item, pos):
if pos <= self.start_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: elif pos < self.end_pos:
for branch_pos, branch in zip(self.branch_starts[::-1], self.branches[::-1]): for branch_pos, branch in zip(self.branch_starts[::-1], self.branches[::-1]):
if pos >= branch_pos: if pos >= branch_pos:
return branch.get_pos_state(pos, item) return branch.get_pos_state(item, pos)
else: 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: if last_state is None:
return None, None return None, None
for branch in self.branches[1:]: 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: if other_state is None or other_state != last_state:
return None, None return None, None
elif last_pos is not other_pos: elif last_pos is not other_pos:
...@@ -148,7 +148,7 @@ class BranchingControlFlow(ControlFlow): ...@@ -148,7 +148,7 @@ class BranchingControlFlow(ControlFlow):
self.branch_starts.append(pos) self.branch_starts.append(pos)
return self.branches[-1] return self.branches[-1]
def to_string(self, indent, limit=None): def to_string(self, indent='', limit=None):
join = "\n%sor\n" % indent join = "\n%sor\n" % indent
s = join.join([branch.to_string(indent+" ", limit=self.incoming) for branch in self.branches]) 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: if self.incoming is not limit and self.incoming is not None:
......
...@@ -818,6 +818,7 @@ class NameNode(AtomicExprNode): ...@@ -818,6 +818,7 @@ class NameNode(AtomicExprNode):
if not self.entry: if not self.entry:
self.entry = env.declare_var(self.name, py_object_type, self.pos) 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, 'initalized'), True)
env.control_flow.set_state(self.pos, (self.name, 'source'), 'assignment')
if self.entry.is_declared_generic: if self.entry.is_declared_generic:
self.result_ctype = py_object_type self.result_ctype = py_object_type
...@@ -947,11 +948,12 @@ class NameNode(AtomicExprNode): ...@@ -947,11 +948,12 @@ class NameNode(AtomicExprNode):
self.entry.name, self.entry.name,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result_code, self.pos)))
elif entry.is_local: 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: if assigned is False:
error(self.pos, "local variable '%s' referenced before assignment" % entry.name) error(self.pos, "local variable '%s' referenced before assignment" % entry.name)
elif assigned is None: elif not Options.init_local_none and assigned is None:
code.putln('/* check %s */' % entry.cname) 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): def generate_assignment_code(self, rhs, code):
#print "NameNode.generate_assignment_code:", self.name ### #print "NameNode.generate_assignment_code:", self.name ###
...@@ -1004,7 +1006,14 @@ class NameNode(AtomicExprNode): ...@@ -1004,7 +1006,14 @@ class NameNode(AtomicExprNode):
#print "...LHS type", self.type, "ctype", self.ctype() ### #print "...LHS type", self.type, "ctype", self.ctype() ###
#print "...RHS type", rhs.type, "ctype", rhs.ctype() ### #print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
rhs.make_owned_reference(code) 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()))) code.putln('%s = %s;' % (self.result_code, rhs.result_as(self.ctype())))
if debug_disposal_code: if debug_disposal_code:
print("NameNode.generate_assignment_code:") print("NameNode.generate_assignment_code:")
......
...@@ -357,6 +357,7 @@ class CNameDeclaratorNode(CDeclaratorNode): ...@@ -357,6 +357,7 @@ class CNameDeclaratorNode(CDeclaratorNode):
self.entry = env.lookup(self.name) self.entry = env.lookup(self.name)
if self.rhs is not None: 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, 'initalized'), True)
env.control_flow.set_state(self.rhs.end_pos(), (self.entry.name, 'source'), 'assignment')
self.entry.used = 1 self.entry.used = 1
if self.type.is_pyobject: if self.type.is_pyobject:
self.entry.init_to_none = False self.entry.init_to_none = False
...@@ -846,7 +847,11 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -846,7 +847,11 @@ class FuncDefNode(StatNode, BlockNode):
code.putln("PyGILState_STATE _save = PyGILState_Ensure();") code.putln("PyGILState_STATE _save = PyGILState_Ensure();")
# ----- Fetch arguments # ----- Fetch arguments
self.generate_argument_parsing_code(env, code) 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 # ----- Initialise local variables
for entry in lenv.var_entries: for entry in lenv.var_entries:
if entry.type.is_pyobject and entry.init_to_none and entry.used: if entry.type.is_pyobject and entry.init_to_none and entry.used:
...@@ -899,9 +904,15 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -899,9 +904,15 @@ class FuncDefNode(StatNode, BlockNode):
#self.return_type.default_value)) #self.return_type.default_value))
# ----- Return cleanup # ----- Return cleanup
code.put_label(code.return_label) 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) code.put_var_decrefs(lenv.var_entries, used_only = 1)
# Decref any increfed args
for entry in lenv.arg_entries: 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) code.put_var_decref(entry)
self.put_stararg_decrefs(code) self.put_stararg_decrefs(code)
if acquire_gil: if acquire_gil:
...@@ -914,7 +925,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -914,7 +925,7 @@ class FuncDefNode(StatNode, BlockNode):
if self.py_func: if self.py_func:
self.py_func.generate_function_definitions(env, code, transforms) self.py_func.generate_function_definitions(env, code, transforms)
self.generate_optarg_wrapper_function(env, code) self.generate_optarg_wrapper_function(env, code)
def put_stararg_decrefs(self, code): def put_stararg_decrefs(self, code):
pass pass
...@@ -925,15 +936,6 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -925,15 +936,6 @@ class FuncDefNode(StatNode, BlockNode):
error(arg.pos, error(arg.pos,
"Argument type '%s' is incomplete" % arg.type) "Argument type '%s' is incomplete" % arg.type)
return env.declare_arg(arg.name, arg.type, arg.pos) 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): def generate_optarg_wrapper_function(self, env, code):
pass pass
...@@ -2535,9 +2537,6 @@ class ContinueStatNode(StatNode): ...@@ -2535,9 +2537,6 @@ class ContinueStatNode(StatNode):
elif not code.continue_label: elif not code.continue_label:
error(self.pos, "continue statement not inside loop") error(self.pos, "continue statement not inside loop")
else: else:
#code.putln(
# "goto %s;" %
# code.continue_label)
code.put_goto(code.continue_label) code.put_goto(code.continue_label)
...@@ -3144,13 +3143,18 @@ class TryExceptStatNode(StatNode): ...@@ -3144,13 +3143,18 @@ class TryExceptStatNode(StatNode):
def analyse_control_flow(self, env): def analyse_control_flow(self, env):
env.start_branching(self.pos) env.start_branching(self.pos)
self.body.analyse_control_flow(env) 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: for except_clause in self.except_clauses:
env.next_branch(except_clause.pos)
except_clause.analyse_control_flow(env) 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: 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) self.else_clause.analyse_control_flow(env)
env.finish_branching(self.end_pos()) env.finish_branching(self.end_pos())
...@@ -3320,8 +3324,8 @@ class TryFinallyStatNode(StatNode): ...@@ -3320,8 +3324,8 @@ class TryFinallyStatNode(StatNode):
env.start_branching(self.pos) env.start_branching(self.pos)
self.body.analyse_control_flow(env) self.body.analyse_control_flow(env)
env.next_branch(self.body.end_pos()) env.next_branch(self.body.end_pos())
env.finish_branching(self.body.end_pos())
self.finally_clause.analyse_control_flow(env) self.finally_clause.analyse_control_flow(env)
env.finish_branching(self.end_pos())
def analyse_declarations(self, env): def analyse_declarations(self, env):
self.body.analyse_declarations(env) self.body.analyse_declarations(env)
......
...@@ -39,3 +39,9 @@ convert_range = 0 ...@@ -39,3 +39,9 @@ convert_range = 0
# lookup on every call. # lookup on every call.
# If this is 0 it simply creates a wrapper. # If this is 0 it simply creates a wrapper.
lookup_module_cpdef = 0 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
...@@ -1033,6 +1033,7 @@ class LocalScope(Scope): ...@@ -1033,6 +1033,7 @@ class LocalScope(Scope):
entry.is_arg = 1 entry.is_arg = 1
#entry.borrowed = 1 # Not using borrowed arg refs for now #entry.borrowed = 1 # Not using borrowed arg refs for now
self.arg_entries.append(entry) self.arg_entries.append(entry)
self.control_flow.set_state((), (name, 'source'), 'arg')
return entry return entry
def declare_var(self, name, type, pos, def declare_var(self, name, type, pos,
...@@ -1042,7 +1043,9 @@ class LocalScope(Scope): ...@@ -1042,7 +1043,9 @@ class LocalScope(Scope):
error(pos, "Local variable cannot be declared %s" % visibility) error(pos, "Local variable cannot be declared %s" % visibility)
entry = Scope.declare_var(self, name, type, pos, entry = Scope.declare_var(self, name, type, pos,
cname, visibility, is_cdef) 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 entry.is_local = 1
self.var_entries.append(entry) self.var_entries.append(entry)
return entry return entry
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment