Skip to content

Commit e25e055

Browse files
committed
Handle mutable references in alias analysis
1 parent f28796e commit e25e055

File tree

2 files changed

+90
-32
lines changed

2 files changed

+90
-32
lines changed

src/comp/middle/alias.rs

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,30 @@ fn check_call(&ctx cx, &@ast::expr f, &vec[@ast::expr] args, &scope sc)
113113
case (ty::ty_native_fn(_, ?args, _)) { args }
114114
};
115115

116-
auto i = 0u;
117116
let vec[def_num] roots = [];
117+
let vec[tup(uint, def_num)] mut_roots = [];
118118
let vec[ty::t] unsafe_ts = [];
119119
let vec[uint] unsafe_t_offsets = [];
120+
121+
auto i = 0u;
120122
for (ty::arg arg_t in arg_ts) {
121123
if (arg_t.mode != ty::mo_val) {
122-
auto root = expr_root(cx, args.(i), false);
124+
auto arg = args.(i);
125+
auto root = expr_root(cx, arg, false);
126+
if (arg_t.mode == ty::mo_alias(true)) {
127+
alt (path_def_id(cx, arg)) {
128+
case (some(?did)) {
129+
vec::push(mut_roots, tup(i, did._1));
130+
}
131+
case (_) {
132+
if (!root.mut_field) {
133+
cx.tcx.sess.span_err
134+
(arg.span, "passing a temporary value or \
135+
immutable field by mutable alias");
136+
}
137+
}
138+
}
139+
}
123140
alt (path_def_id(cx, root.ex)) {
124141
case (some(?did)) { vec::push(roots, did._1); }
125142
case (_) {}
@@ -154,19 +171,31 @@ fn check_call(&ctx cx, &@ast::expr f, &vec[@ast::expr] args, &scope sc)
154171
j += 1u;
155172
auto i = 0u;
156173
for (ty::arg arg_t in arg_ts) {
174+
auto mut_alias = arg_t.mode == ty::mo_alias(true);
157175
if (i != offset &&
158-
// FIXME false should be replace with mutability of alias
159-
ty_can_unsafely_include(cx, unsafe, arg_t.ty, false)) {
176+
ty_can_unsafely_include(cx, unsafe, arg_t.ty, mut_alias)) {
160177
cx.tcx.sess.span_err
161178
(args.(i).span, #fmt("argument %u may alias with \
162179
argument %u, which is not immutably rooted", i, offset));
163180
}
164181
i += 1u;
165182
}
166183
}
167-
// FIXME when mutable aliases can be distinguished, go over the args again
168-
// and ensure that we're not passing a root variable by mutable alias
169-
// (using roots and the scope root vars).
184+
185+
// Ensure we're not passing a root by mutable alias.
186+
for (tup(uint, def_num) root in mut_roots) {
187+
auto mut_alias_to_root = vec::count(root._1, roots) > 1u;
188+
for (restrict r in sc.rs) {
189+
if (vec::member(root._1, r.root_vars)) {
190+
mut_alias_to_root = true;
191+
}
192+
}
193+
if (mut_alias_to_root) {
194+
cx.tcx.sess.span_err
195+
(args.(root._0).span, "passing a mutable alias to a \
196+
variable that roots another alias");
197+
}
198+
}
170199

171200
ret rec(root_vars = roots, unsafe_ts = unsafe_ts);
172201
}
@@ -300,25 +329,16 @@ fn check_assign(&@ctx cx, &@ast::expr dest, &@ast::expr src,
300329
alt (dest.node) {
301330
case (ast::expr_path(?p, ?ann)) {
302331
auto dnum = ast::def_id_of_def(cx.dm.get(ann.id))._1;
303-
304-
for (tup(def_num, ast::mode) arg in sc.args) {
305-
if (arg._0 == dnum && arg._1 == ast::alias(false)) {
306-
cx.tcx.sess.span_err
307-
(dest.span, "assigning to immutable alias");
308-
}
332+
if (is_immutable_alias(sc, dnum)) {
333+
cx.tcx.sess.span_err
334+
(dest.span, "assigning to immutable alias");
309335
}
310336

311337
auto var_t = ty::expr_ty(*cx.tcx, dest);
312338
for (restrict r in sc.rs) {
313339
if (vec::member(dnum, r.root_vars)) {
314340
r.ok = overwritten(dest.span, p);
315341
}
316-
for (def_num bnd in r.bindings) {
317-
if (dnum == bnd) {
318-
cx.tcx.sess.span_err
319-
(dest.span, "assigning to immutable alias");
320-
}
321-
}
322342
}
323343
check_var(*cx, dest, p, ann, true, sc);
324344
}
@@ -328,6 +348,16 @@ fn check_assign(&@ctx cx, &@ast::expr dest, &@ast::expr src,
328348
}
329349
}
330350

351+
fn is_immutable_alias(&scope sc, def_num dnum) -> bool {
352+
for (tup(def_num, ast::mode) arg in sc.args) {
353+
if (arg._0 == dnum && arg._1 == ast::alias(false)) { ret true; }
354+
}
355+
for (restrict r in sc.rs) {
356+
if (vec::member(dnum, r.bindings)) { ret true; }
357+
}
358+
ret false;
359+
}
360+
331361
fn test_scope(&ctx cx, &scope sc, &restrict r, &ast::path p) {
332362
auto prob = r.ok;
333363
for (uint dep in r.depends_on) {
@@ -364,13 +394,18 @@ fn deps(&scope sc, vec[def_num] roots) -> vec[uint] {
364394
}
365395

366396
fn expr_root(&ctx cx, @ast::expr ex, bool autoderef)
367-
-> rec(@ast::expr ex, option::t[ty::t] inner_mut, bool mut_in_box) {
397+
-> rec(@ast::expr ex,
398+
option::t[ty::t] inner_mut,
399+
bool mut_in_box,
400+
bool mut_field) {
368401
let option::t[ty::t] mut = none;
369402
// This is not currently used but would make it possible to be more
370403
// liberal -- only stuff in a mutable box needs full type-inclusion
371404
// checking, things that aren't in a box need only be checked against
372405
// locally live aliases and their root.
373406
auto mut_in_box = false;
407+
auto mut_fld = false;
408+
auto depth = 0;
374409
while (true) {
375410
alt ({ex.node}) {
376411
case (ast::expr_field(?base, ?ident, _)) {
@@ -379,15 +414,19 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef)
379414
alt (ty::struct(*cx.tcx, auto_unbox.t)) {
380415
case (ty::ty_tup(?fields)) {
381416
auto fnm = ty::field_num(cx.tcx.sess, ex.span, ident);
382-
if (fields.(fnm).mut != ast::imm && is_none(mut)) {
383-
mut = some(auto_unbox.t);
417+
if (fields.(fnm).mut != ast::imm) {
418+
if (is_none(mut)) { mut = some(auto_unbox.t); }
419+
if (depth == 0) { mut_fld = true; }
384420
}
385421
}
386422
case (ty::ty_rec(?fields)) {
387423
for (ty::field fld in fields) {
388424
if (str::eq(ident, fld.ident)) {
389-
if (fld.mt.mut != ast::imm && is_none(mut)) {
390-
mut = some(auto_unbox.t);
425+
if (fld.mt.mut != ast::imm) {
426+
if (is_none(mut)) {
427+
mut = some(auto_unbox.t);
428+
}
429+
if (depth == 0) { mut_fld = true; }
391430
}
392431
break;
393432
}
@@ -406,26 +445,26 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef)
406445
auto auto_unbox = maybe_auto_unbox(cx, base_t);
407446
alt (ty::struct(*cx.tcx, auto_unbox.t)) {
408447
case (ty::ty_vec(?mt)) {
409-
if (mt.mut != ast::imm && is_none(mut)) {
410-
mut = some(auto_unbox.t);
448+
if (mt.mut != ast::imm) {
449+
if (is_none(mut)) { mut = some(auto_unbox.t); }
450+
if (depth == 0) { mut_fld = true; }
411451
}
412452
}
413453
}
414454
if (auto_unbox.done) {
415455
if (!is_none(mut)) { mut_in_box = true; }
416456
else if (auto_unbox.mut) { mut = some(base_t); }
417457
}
418-
if (auto_unbox.done && !is_none(mut)) {
419-
}
420458
ex = base;
421459
}
422460
case (ast::expr_unary(?op, ?base, _)) {
423461
if (op == ast::deref) {
424462
auto base_t = ty::expr_ty(*cx.tcx, base);
425463
alt (ty::struct(*cx.tcx, base_t)) {
426464
case (ty::ty_box(?mt)) {
427-
if (mt.mut != ast::imm && is_none(mut)) {
428-
mut = some(base_t);
465+
if (mt.mut != ast::imm) {
466+
if (is_none(mut)) { mut = some(base_t); }
467+
if (depth == 0) { mut_fld = true; }
429468
}
430469
if (!is_none(mut)) {
431470
mut_in_box = true;
@@ -439,16 +478,23 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef)
439478
}
440479
case (_) { break; }
441480
}
481+
depth += 1;
442482
}
443483
if (autoderef) {
444484
auto ex_t = ty::expr_ty(*cx.tcx, ex);
445485
auto auto_unbox = maybe_auto_unbox(cx, ex_t);
446486
if (auto_unbox.done) {
447487
if (!is_none(mut)) { mut_in_box = true; }
448-
else if (auto_unbox.mut) { mut = some(ex_t); }
488+
else if (auto_unbox.mut) {
489+
mut = some(ex_t);
490+
if (depth == 0) { mut_fld = true; }
491+
}
449492
}
450493
}
451-
ret rec(ex = ex, inner_mut = mut, mut_in_box = mut_in_box);
494+
ret rec(ex = ex,
495+
inner_mut = mut,
496+
mut_in_box = mut_in_box,
497+
mut_field = mut_fld);
452498
}
453499

454500
fn maybe_auto_unbox(&ctx cx, &ty::t t)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// xfail-stage0
2+
// error-pattern:mutable alias to a variable that roots another alias
3+
4+
fn f(&int a, &mutable int b) -> int {
5+
b += 1;
6+
ret a + b;
7+
}
8+
9+
fn main() {
10+
auto i = 4;
11+
log f(i, i);
12+
}

0 commit comments

Comments
 (0)