entries set tracked entries
loops list stack for loop descriptors
exceptions list stack for exception descriptors
-
+ outer_flow ControlFlow the ControlFlow instance of the outer closure
"""
- def __init__(self):
+ def __init__(self, outer_flow=None):
self.blocks = set()
self.entries = set()
self.loops = []
self.exceptions = []
+ self.outer_flow = outer_flow
self.entry_point = ControlBlock()
self.exit_point = ExitBlock()
self.blocks.add(self.exit_point)
self.block.positions.add(node.pos[:2])
def mark_assignment(self, lhs, rhs, entry):
- entry = entry.defining_entry
if self.block and self.is_tracked(entry):
assignment = NameAssignment(lhs, rhs, entry)
self.block.stats.append(assignment)
self.entries.add(entry)
def mark_argument(self, lhs, rhs, entry):
- entry = entry.defining_entry
if self.block and self.is_tracked(entry):
assignment = Argument(lhs, rhs, entry)
self.block.stats.append(assignment)
self.entries.add(entry)
def mark_deletion(self, node, entry):
- entry = entry.defining_entry
if self.block and self.is_tracked(entry):
assignment = NameDeletion(node, entry)
self.block.stats.append(assignment)
self.entries.add(entry)
def mark_reference(self, node, entry):
- entry = entry.defining_entry
if self.block and self.is_tracked(entry):
self.block.stats.append(NameReference(node, entry))
# Local variable is definitely bound after this reference
ret = set()
assmts = self.assmts[entry]
if istate & assmts.bit:
- ret.add(Uninitialized)
+ if entry.from_closure:
+ ret.add(Unknown)
+ else:
+ ret.add(Uninitialized)
for assmt in assmts.stats:
if istate & assmt.bit:
ret.add(assmt)
class Uninitialized(object):
- pass
+ """Definitely not initialised yet."""
+
+
+class Unknown(object):
+ """Coming from outer closure, might be initialised or not."""
+
class NameReference(object):
def __init__(self, node, entry):
self.cf_maybe_null = True
if not state:
self.cf_is_null = True
+ elif Unknown in state:
+ state.discard(Unknown)
+ self.cf_maybe_null = True
else:
if len(state) == 1:
self.is_single = True
stat.node.cf_state.update(state)
if not stat.node.allow_null:
i_state &= ~i_assmts.bit
+ # after successful read, the state is known to be initialised
state.discard(Uninitialized)
+ state.discard(Unknown)
for assmt in state:
assmt.refs.add(stat)
node.cf_is_null = True
else:
node.cf_is_null = False
+ elif Unknown in node.cf_state:
+ node.cf_maybe_null = True
else:
node.cf_is_null = False
node.cf_maybe_null = False
node.cf_is_null = True
if node.allow_null or entry.from_closure or entry.is_pyclass_attr:
pass # Can be uninitialized here
- elif entry.in_closure:
- pass # not smart enough to get this right
elif node.cf_is_null:
if (entry.type.is_pyobject or entry.type.is_unspecified or
entry.error_on_uninitialized):
node.pos,
"local variable '%s' might be referenced before assignment"
% entry.name)
+ elif Unknown in node.cf_state:
+ # TODO: better cross-closure analysis to know when inner functions
+ # are being called before a variable is being set, and when
+ # a variable is known to be set before even defining the
+ # inner function, etc.
+ node.cf_maybe_null = True
else:
node.cf_is_null = False
node.cf_maybe_null = False
self.env_stack.append(self.env)
self.env = node.local_scope
self.stack.append(self.flow)
- self.flow = ControlFlow()
+ self.flow = ControlFlow(self.flow)
# Collect all entries
for entry in node.local_scope.entries.values():