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

More control flow work

parent 670cda8e
......@@ -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:
......
......@@ -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:")
......
......@@ -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)
......
......@@ -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
......@@ -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
......
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