Prohibit assignment to upvars in lambdas. Closes #805.
This commit is contained in:
parent
a26c027731
commit
c5d55ef918
4 changed files with 58 additions and 5 deletions
|
@ -57,7 +57,29 @@ fn visit_fn(cx: &@ctx, f: &ast::_fn, tp: &ast::ty_param[], sp: &span,
|
|||
for arg_: ast::arg in f.decl.inputs {
|
||||
cx.local_map.insert(arg_.id, arg(arg_.mode));
|
||||
}
|
||||
v.visit_block(f.body, @~[], v);
|
||||
let scope = alt (f.proto) {
|
||||
// Blocks need to obey any restrictions from the enclosing scope.
|
||||
ast::proto_block. { sc }
|
||||
// Closures need to prohibit writing to any of the upvars.
|
||||
// This doesn't seem like a particularly clean way to do this.
|
||||
ast::proto_closure. {
|
||||
let dnums = ~[];
|
||||
for each nid in freevars::get_freevar_defs(cx.tcx, id).keys() {
|
||||
dnums += ~[nid];
|
||||
}
|
||||
@~[@{root_vars: ~[],
|
||||
// I'm not sure if there is anything sensical to put here
|
||||
block_defnum: 0,
|
||||
bindings: dnums,
|
||||
tys: ~[],
|
||||
depends_on: ~[],
|
||||
mutable ok: valid}]
|
||||
}
|
||||
// Non capturing functions start out fresh.
|
||||
_ { @~[] }
|
||||
};
|
||||
|
||||
v.visit_block(f.body, scope, v);
|
||||
}
|
||||
|
||||
fn visit_item(cx: &@ctx, i: &@ast::item, sc: &scope, v: &vt[scope]) {
|
||||
|
|
|
@ -18,7 +18,7 @@ export freevar_set;
|
|||
export freevar_map;
|
||||
export get_freevar_info;
|
||||
export get_freevars;
|
||||
export get_freevar_refs;
|
||||
export get_freevar_defs;
|
||||
export has_freevars;
|
||||
export is_freevar_of;
|
||||
export def_lookup;
|
||||
|
@ -145,17 +145,17 @@ fn get_freevar_info(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_info {
|
|||
some(d) { ret d; }
|
||||
}
|
||||
}
|
||||
fn get_freevar_refs(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_set {
|
||||
fn get_freevar_defs(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_set {
|
||||
ret get_freevar_info(tcx, fid).defs;
|
||||
}
|
||||
fn get_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> @ast::node_id[] {
|
||||
ret get_freevar_info(tcx, fid).refs;
|
||||
}
|
||||
fn has_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> bool {
|
||||
ret get_freevar_refs(tcx, fid).size() != 0u;
|
||||
ret get_freevar_defs(tcx, fid).size() != 0u;
|
||||
}
|
||||
fn is_freevar_of(tcx: &ty::ctxt, def: ast::node_id, f: ast::node_id) -> bool {
|
||||
ret get_freevar_refs(tcx, f).contains_key(def);
|
||||
ret get_freevar_defs(tcx, f).contains_key(def);
|
||||
}
|
||||
fn def_lookup(tcx: &ty::ctxt, f: ast::node_id, id: ast::node_id) ->
|
||||
option::t[ast::def] {
|
||||
|
|
16
src/test/compile-fail/lambda-mutate-nested.rs
Normal file
16
src/test/compile-fail/lambda-mutate-nested.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// error-pattern:assigning to immutable alias
|
||||
// Make sure that nesting a block within a lambda doesn't let us
|
||||
// mutate upvars from a lambda.
|
||||
fn main() {
|
||||
let i = 0;
|
||||
let ctr = lambda() -> int {
|
||||
block() { i = i + 1; }();
|
||||
ret i;
|
||||
};
|
||||
log_err ctr();
|
||||
log_err ctr();
|
||||
log_err ctr();
|
||||
log_err ctr();
|
||||
log_err ctr();
|
||||
log_err i;
|
||||
}
|
15
src/test/compile-fail/lambda-mutate.rs
Normal file
15
src/test/compile-fail/lambda-mutate.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
// error-pattern:assigning to immutable alias
|
||||
// Make sure we can't write to upvars from lambdas
|
||||
fn main() {
|
||||
let i = 0;
|
||||
let ctr = lambda() -> int {
|
||||
i = i + 1;
|
||||
ret i;
|
||||
};
|
||||
log_err ctr();
|
||||
log_err ctr();
|
||||
log_err ctr();
|
||||
log_err ctr();
|
||||
log_err ctr();
|
||||
log_err i;
|
||||
}
|
Loading…
Reference in a new issue