Skip to content

Commit 7e3f91c

Browse files
author
whitequark
committedJul 19, 2015
Teach closures to LocalAccessValidator.
1 parent 2c010b1 commit 7e3f91c

File tree

2 files changed

+65
-31
lines changed

2 files changed

+65
-31
lines changed
 

Diff for: ‎artiq/compiler/validators/local_access.py

+58-31
Original file line numberDiff line numberDiff line change
@@ -73,27 +73,38 @@ def merge_state(a, b):
7373
# Update the state based on block contents, while validating
7474
# that no access to uninitialized variables will be done.
7575
for insn in block.instructions:
76+
def pred_at_fault(env, var_name):
77+
# Find out where the uninitialized state comes from.
78+
for pred, pred_state in zip(forward_edge_preds, pred_states):
79+
if not pred_state[env][var_name]:
80+
return pred
81+
82+
# It's the entry block and it was never initialized.
83+
return None
84+
7685
if isinstance(insn, (ir.SetLocal, ir.GetLocal)) and \
7786
"." not in insn.var_name:
7887
env, var_name = insn.environment(), insn.var_name
79-
assert env in block_state
80-
assert var_name in block_state[env]
8188

82-
if isinstance(insn, ir.SetLocal):
83-
# We've just initialized it.
84-
block_state[env][var_name] = True
85-
else: # isinstance(insn, ir.GetLocal)
89+
# Make sure that the variable is defined in the scope of this function.
90+
if env in block_state and var_name in block_state[env]:
91+
if isinstance(insn, ir.SetLocal):
92+
# We've just initialized it.
93+
block_state[env][var_name] = True
94+
else: # isinstance(insn, ir.GetLocal)
95+
if not block_state[env][var_name]:
96+
# Oops, accessing it uninitialized.
97+
self._uninitialized_access(insn, var_name,
98+
pred_at_fault(env, var_name))
99+
100+
if isinstance(insn, ir.Closure):
101+
env = insn.environment()
102+
for var_name in block_state[env]:
86103
if not block_state[env][var_name]:
87-
# Oops, accessing it uninitialized. Find out where
88-
# the uninitialized state comes from.
89-
pred_at_fault = None
90-
for pred, pred_state in zip(forward_edge_preds, pred_states):
91-
if not pred_state[env][var_name]:
92-
pred_at_fault = pred
93-
assert pred_at_fault is not None
94-
95-
# Report the error.
96-
self._uninitialized_access(insn, pred_at_fault)
104+
# A closure would capture this variable while it is not always
105+
# initialized. Note that this check is transitive.
106+
self._uninitialized_access(insn, var_name,
107+
pred_at_fault(env, var_name))
97108

98109
# Save the state.
99110
state[block] = block_state
@@ -103,19 +114,35 @@ def merge_state(a, b):
103114
for block in func.basic_blocks:
104115
traverse(block)
105116

106-
def _uninitialized_access(self, insn, pred_at_fault):
107-
uninitialized_loc = None
108-
for pred_insn in reversed(pred_at_fault.instructions):
109-
if pred_insn.loc is not None:
110-
uninitialized_loc = pred_insn.loc.begin()
111-
break
112-
assert uninitialized_loc is not None
113-
114-
note = diagnostic.Diagnostic("note",
115-
"variable is not initialized when control flows from this point", {},
116-
uninitialized_loc)
117-
diag = diagnostic.Diagnostic("error",
118-
"variable '{name}' is not always initialized at this point",
119-
{"name": insn.var_name},
120-
insn.loc, notes=[note])
117+
def _uninitialized_access(self, insn, var_name, pred_at_fault):
118+
if pred_at_fault is not None:
119+
uninitialized_loc = None
120+
for pred_insn in reversed(pred_at_fault.instructions):
121+
if pred_insn.loc is not None:
122+
uninitialized_loc = pred_insn.loc.begin()
123+
break
124+
assert uninitialized_loc is not None
125+
126+
note = diagnostic.Diagnostic("note",
127+
"variable is not initialized when control flows from this point", {},
128+
uninitialized_loc)
129+
else:
130+
note = None
131+
132+
if note is not None:
133+
notes = [note]
134+
else:
135+
notes = []
136+
137+
if isinstance(insn, ir.Closure):
138+
diag = diagnostic.Diagnostic("error",
139+
"variable '{name}' can be captured in a closure uninitialized here",
140+
{"name": var_name},
141+
insn.loc, notes=notes)
142+
else:
143+
diag = diagnostic.Diagnostic("error",
144+
"variable '{name}' is not always initialized here",
145+
{"name": var_name},
146+
insn.loc, notes=notes)
147+
121148
self.engine.process(diag)

Diff for: ‎lit-test/compiler/local_access/invalid.py

+7
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,10 @@
1818
t = 1
1919
# CHECK-L: ${LINE:+1}: error: variable 't' is not always initialized
2020
-t
21+
22+
# CHECK-L: ${LINE:+1}: error: variable 't' can be captured in a closure uninitialized
23+
lambda: t
24+
25+
# CHECK-L: ${LINE:+1}: error: variable 't' can be captured in a closure uninitialized
26+
def f():
27+
return t

0 commit comments

Comments
 (0)
Please sign in to comment.