Skip to content

Commit c5d55ef

Browse files
committed
Prohibit assignment to upvars in lambdas. Closes #805.
1 parent a26c027 commit c5d55ef

File tree

4 files changed

+58
-5
lines changed

4 files changed

+58
-5
lines changed

src/comp/middle/alias.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,29 @@ fn visit_fn(cx: &@ctx, f: &ast::_fn, tp: &ast::ty_param[], sp: &span,
5757
for arg_: ast::arg in f.decl.inputs {
5858
cx.local_map.insert(arg_.id, arg(arg_.mode));
5959
}
60-
v.visit_block(f.body, @~[], v);
60+
let scope = alt (f.proto) {
61+
// Blocks need to obey any restrictions from the enclosing scope.
62+
ast::proto_block. { sc }
63+
// Closures need to prohibit writing to any of the upvars.
64+
// This doesn't seem like a particularly clean way to do this.
65+
ast::proto_closure. {
66+
let dnums = ~[];
67+
for each nid in freevars::get_freevar_defs(cx.tcx, id).keys() {
68+
dnums += ~[nid];
69+
}
70+
@~[@{root_vars: ~[],
71+
// I'm not sure if there is anything sensical to put here
72+
block_defnum: 0,
73+
bindings: dnums,
74+
tys: ~[],
75+
depends_on: ~[],
76+
mutable ok: valid}]
77+
}
78+
// Non capturing functions start out fresh.
79+
_ { @~[] }
80+
};
81+
82+
v.visit_block(f.body, scope, v);
6183
}
6284

6385
fn visit_item(cx: &@ctx, i: &@ast::item, sc: &scope, v: &vt[scope]) {

src/comp/middle/freevars.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export freevar_set;
1818
export freevar_map;
1919
export get_freevar_info;
2020
export get_freevars;
21-
export get_freevar_refs;
21+
export get_freevar_defs;
2222
export has_freevars;
2323
export is_freevar_of;
2424
export def_lookup;
@@ -145,17 +145,17 @@ fn get_freevar_info(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_info {
145145
some(d) { ret d; }
146146
}
147147
}
148-
fn get_freevar_refs(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_set {
148+
fn get_freevar_defs(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_set {
149149
ret get_freevar_info(tcx, fid).defs;
150150
}
151151
fn get_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> @ast::node_id[] {
152152
ret get_freevar_info(tcx, fid).refs;
153153
}
154154
fn has_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> bool {
155-
ret get_freevar_refs(tcx, fid).size() != 0u;
155+
ret get_freevar_defs(tcx, fid).size() != 0u;
156156
}
157157
fn is_freevar_of(tcx: &ty::ctxt, def: ast::node_id, f: ast::node_id) -> bool {
158-
ret get_freevar_refs(tcx, f).contains_key(def);
158+
ret get_freevar_defs(tcx, f).contains_key(def);
159159
}
160160
fn def_lookup(tcx: &ty::ctxt, f: ast::node_id, id: ast::node_id) ->
161161
option::t[ast::def] {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// error-pattern:assigning to immutable alias
2+
// Make sure that nesting a block within a lambda doesn't let us
3+
// mutate upvars from a lambda.
4+
fn main() {
5+
let i = 0;
6+
let ctr = lambda() -> int {
7+
block() { i = i + 1; }();
8+
ret i;
9+
};
10+
log_err ctr();
11+
log_err ctr();
12+
log_err ctr();
13+
log_err ctr();
14+
log_err ctr();
15+
log_err i;
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// error-pattern:assigning to immutable alias
2+
// Make sure we can't write to upvars from lambdas
3+
fn main() {
4+
let i = 0;
5+
let ctr = lambda() -> int {
6+
i = i + 1;
7+
ret i;
8+
};
9+
log_err ctr();
10+
log_err ctr();
11+
log_err ctr();
12+
log_err ctr();
13+
log_err ctr();
14+
log_err i;
15+
}

0 commit comments

Comments
 (0)