Prohibit assignment to upvars in lambdas. Closes #805.

This commit is contained in:
Michael Sullivan 2011-08-04 19:31:47 -07:00
parent a26c027731
commit c5d55ef918
4 changed files with 58 additions and 5 deletions

View file

@ -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]) {

View file

@ -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] {

View 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;
}

View 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;
}