mirror of https://github.com/m-labs/artiq.git
Teach closures to LocalAccessValidator.
This commit is contained in:
parent
2c010b10ee
commit
7e3f91c0bb
|
@ -73,27 +73,38 @@ class LocalAccessValidator:
|
||||||
# Update the state based on block contents, while validating
|
# Update the state based on block contents, while validating
|
||||||
# that no access to uninitialized variables will be done.
|
# that no access to uninitialized variables will be done.
|
||||||
for insn in block.instructions:
|
for insn in block.instructions:
|
||||||
|
def pred_at_fault(env, var_name):
|
||||||
|
# Find out where the uninitialized state comes from.
|
||||||
|
for pred, pred_state in zip(forward_edge_preds, pred_states):
|
||||||
|
if not pred_state[env][var_name]:
|
||||||
|
return pred
|
||||||
|
|
||||||
|
# It's the entry block and it was never initialized.
|
||||||
|
return None
|
||||||
|
|
||||||
if isinstance(insn, (ir.SetLocal, ir.GetLocal)) and \
|
if isinstance(insn, (ir.SetLocal, ir.GetLocal)) and \
|
||||||
"." not in insn.var_name:
|
"." not in insn.var_name:
|
||||||
env, var_name = insn.environment(), insn.var_name
|
env, var_name = insn.environment(), insn.var_name
|
||||||
assert env in block_state
|
|
||||||
assert var_name in block_state[env]
|
|
||||||
|
|
||||||
|
# Make sure that the variable is defined in the scope of this function.
|
||||||
|
if env in block_state and var_name in block_state[env]:
|
||||||
if isinstance(insn, ir.SetLocal):
|
if isinstance(insn, ir.SetLocal):
|
||||||
# We've just initialized it.
|
# We've just initialized it.
|
||||||
block_state[env][var_name] = True
|
block_state[env][var_name] = True
|
||||||
else: # isinstance(insn, ir.GetLocal)
|
else: # isinstance(insn, ir.GetLocal)
|
||||||
if not block_state[env][var_name]:
|
if not block_state[env][var_name]:
|
||||||
# Oops, accessing it uninitialized. Find out where
|
# Oops, accessing it uninitialized.
|
||||||
# the uninitialized state comes from.
|
self._uninitialized_access(insn, var_name,
|
||||||
pred_at_fault = None
|
pred_at_fault(env, var_name))
|
||||||
for pred, pred_state in zip(forward_edge_preds, pred_states):
|
|
||||||
if not pred_state[env][var_name]:
|
|
||||||
pred_at_fault = pred
|
|
||||||
assert pred_at_fault is not None
|
|
||||||
|
|
||||||
# Report the error.
|
if isinstance(insn, ir.Closure):
|
||||||
self._uninitialized_access(insn, pred_at_fault)
|
env = insn.environment()
|
||||||
|
for var_name in block_state[env]:
|
||||||
|
if not block_state[env][var_name]:
|
||||||
|
# A closure would capture this variable while it is not always
|
||||||
|
# initialized. Note that this check is transitive.
|
||||||
|
self._uninitialized_access(insn, var_name,
|
||||||
|
pred_at_fault(env, var_name))
|
||||||
|
|
||||||
# Save the state.
|
# Save the state.
|
||||||
state[block] = block_state
|
state[block] = block_state
|
||||||
|
@ -103,7 +114,8 @@ class LocalAccessValidator:
|
||||||
for block in func.basic_blocks:
|
for block in func.basic_blocks:
|
||||||
traverse(block)
|
traverse(block)
|
||||||
|
|
||||||
def _uninitialized_access(self, insn, pred_at_fault):
|
def _uninitialized_access(self, insn, var_name, pred_at_fault):
|
||||||
|
if pred_at_fault is not None:
|
||||||
uninitialized_loc = None
|
uninitialized_loc = None
|
||||||
for pred_insn in reversed(pred_at_fault.instructions):
|
for pred_insn in reversed(pred_at_fault.instructions):
|
||||||
if pred_insn.loc is not None:
|
if pred_insn.loc is not None:
|
||||||
|
@ -114,8 +126,23 @@ class LocalAccessValidator:
|
||||||
note = diagnostic.Diagnostic("note",
|
note = diagnostic.Diagnostic("note",
|
||||||
"variable is not initialized when control flows from this point", {},
|
"variable is not initialized when control flows from this point", {},
|
||||||
uninitialized_loc)
|
uninitialized_loc)
|
||||||
|
else:
|
||||||
|
note = None
|
||||||
|
|
||||||
|
if note is not None:
|
||||||
|
notes = [note]
|
||||||
|
else:
|
||||||
|
notes = []
|
||||||
|
|
||||||
|
if isinstance(insn, ir.Closure):
|
||||||
diag = diagnostic.Diagnostic("error",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"variable '{name}' is not always initialized at this point",
|
"variable '{name}' can be captured in a closure uninitialized here",
|
||||||
{"name": insn.var_name},
|
{"name": var_name},
|
||||||
insn.loc, notes=[note])
|
insn.loc, notes=notes)
|
||||||
|
else:
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"variable '{name}' is not always initialized here",
|
||||||
|
{"name": var_name},
|
||||||
|
insn.loc, notes=notes)
|
||||||
|
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
|
|
|
@ -18,3 +18,10 @@ else:
|
||||||
t = 1
|
t = 1
|
||||||
# CHECK-L: ${LINE:+1}: error: variable 't' is not always initialized
|
# CHECK-L: ${LINE:+1}: error: variable 't' is not always initialized
|
||||||
-t
|
-t
|
||||||
|
|
||||||
|
# CHECK-L: ${LINE:+1}: error: variable 't' can be captured in a closure uninitialized
|
||||||
|
lambda: t
|
||||||
|
|
||||||
|
# CHECK-L: ${LINE:+1}: error: variable 't' can be captured in a closure uninitialized
|
||||||
|
def f():
|
||||||
|
return t
|
||||||
|
|
Loading…
Reference in New Issue