diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index 4b4b54cad2a..d7cf80c0464 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -113,13 +113,30 @@ fn check_call(&ctx cx, &@ast::expr f, &vec[@ast::expr] args, &scope sc) case (ty::ty_native_fn(_, ?args, _)) { args } }; - auto i = 0u; let vec[def_num] roots = []; + let vec[tup(uint, def_num)] mut_roots = []; let vec[ty::t] unsafe_ts = []; let vec[uint] unsafe_t_offsets = []; + + auto i = 0u; for (ty::arg arg_t in arg_ts) { if (arg_t.mode != ty::mo_val) { - auto root = expr_root(cx, args.(i), false); + auto arg = args.(i); + auto root = expr_root(cx, arg, false); + if (arg_t.mode == ty::mo_alias(true)) { + alt (path_def_id(cx, arg)) { + case (some(?did)) { + vec::push(mut_roots, tup(i, did._1)); + } + case (_) { + if (!root.mut_field) { + cx.tcx.sess.span_err + (arg.span, "passing a temporary value or \ + immutable field by mutable alias"); + } + } + } + } alt (path_def_id(cx, root.ex)) { case (some(?did)) { vec::push(roots, did._1); } case (_) {} @@ -154,9 +171,9 @@ fn check_call(&ctx cx, &@ast::expr f, &vec[@ast::expr] args, &scope sc) j += 1u; auto i = 0u; for (ty::arg arg_t in arg_ts) { + auto mut_alias = arg_t.mode == ty::mo_alias(true); if (i != offset && - // FIXME false should be replace with mutability of alias - ty_can_unsafely_include(cx, unsafe, arg_t.ty, false)) { + ty_can_unsafely_include(cx, unsafe, arg_t.ty, mut_alias)) { cx.tcx.sess.span_err (args.(i).span, #fmt("argument %u may alias with \ argument %u, which is not immutably rooted", i, offset)); @@ -164,9 +181,21 @@ fn check_call(&ctx cx, &@ast::expr f, &vec[@ast::expr] args, &scope sc) i += 1u; } } - // FIXME when mutable aliases can be distinguished, go over the args again - // and ensure that we're not passing a root variable by mutable alias - // (using roots and the scope root vars). + + // Ensure we're not passing a root by mutable alias. + for (tup(uint, def_num) root in mut_roots) { + auto mut_alias_to_root = vec::count(root._1, roots) > 1u; + for (restrict r in sc.rs) { + if (vec::member(root._1, r.root_vars)) { + mut_alias_to_root = true; + } + } + if (mut_alias_to_root) { + cx.tcx.sess.span_err + (args.(root._0).span, "passing a mutable alias to a \ + variable that roots another alias"); + } + } ret rec(root_vars = roots, unsafe_ts = unsafe_ts); } @@ -300,12 +329,9 @@ fn check_assign(&@ctx cx, &@ast::expr dest, &@ast::expr src, alt (dest.node) { case (ast::expr_path(?p, ?ann)) { auto dnum = ast::def_id_of_def(cx.dm.get(ann.id))._1; - - for (tup(def_num, ast::mode) arg in sc.args) { - if (arg._0 == dnum && arg._1 == ast::alias(false)) { - cx.tcx.sess.span_err - (dest.span, "assigning to immutable alias"); - } + if (is_immutable_alias(sc, dnum)) { + cx.tcx.sess.span_err + (dest.span, "assigning to immutable alias"); } auto var_t = ty::expr_ty(*cx.tcx, dest); @@ -313,12 +339,6 @@ fn check_assign(&@ctx cx, &@ast::expr dest, &@ast::expr src, if (vec::member(dnum, r.root_vars)) { r.ok = overwritten(dest.span, p); } - for (def_num bnd in r.bindings) { - if (dnum == bnd) { - cx.tcx.sess.span_err - (dest.span, "assigning to immutable alias"); - } - } } check_var(*cx, dest, p, ann, true, sc); } @@ -328,6 +348,16 @@ fn check_assign(&@ctx cx, &@ast::expr dest, &@ast::expr src, } } +fn is_immutable_alias(&scope sc, def_num dnum) -> bool { + for (tup(def_num, ast::mode) arg in sc.args) { + if (arg._0 == dnum && arg._1 == ast::alias(false)) { ret true; } + } + for (restrict r in sc.rs) { + if (vec::member(dnum, r.bindings)) { ret true; } + } + ret false; +} + fn test_scope(&ctx cx, &scope sc, &restrict r, &ast::path p) { auto prob = r.ok; for (uint dep in r.depends_on) { @@ -364,13 +394,18 @@ fn deps(&scope sc, vec[def_num] roots) -> vec[uint] { } fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) - -> rec(@ast::expr ex, option::t[ty::t] inner_mut, bool mut_in_box) { + -> rec(@ast::expr ex, + option::t[ty::t] inner_mut, + bool mut_in_box, + bool mut_field) { let option::t[ty::t] mut = none; // This is not currently used but would make it possible to be more // liberal -- only stuff in a mutable box needs full type-inclusion // checking, things that aren't in a box need only be checked against // locally live aliases and their root. auto mut_in_box = false; + auto mut_fld = false; + auto depth = 0; while (true) { alt ({ex.node}) { case (ast::expr_field(?base, ?ident, _)) { @@ -379,15 +414,19 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) alt (ty::struct(*cx.tcx, auto_unbox.t)) { case (ty::ty_tup(?fields)) { auto fnm = ty::field_num(cx.tcx.sess, ex.span, ident); - if (fields.(fnm).mut != ast::imm && is_none(mut)) { - mut = some(auto_unbox.t); + if (fields.(fnm).mut != ast::imm) { + if (is_none(mut)) { mut = some(auto_unbox.t); } + if (depth == 0) { mut_fld = true; } } } case (ty::ty_rec(?fields)) { for (ty::field fld in fields) { if (str::eq(ident, fld.ident)) { - if (fld.mt.mut != ast::imm && is_none(mut)) { - mut = some(auto_unbox.t); + if (fld.mt.mut != ast::imm) { + if (is_none(mut)) { + mut = some(auto_unbox.t); + } + if (depth == 0) { mut_fld = true; } } break; } @@ -406,8 +445,9 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) auto auto_unbox = maybe_auto_unbox(cx, base_t); alt (ty::struct(*cx.tcx, auto_unbox.t)) { case (ty::ty_vec(?mt)) { - if (mt.mut != ast::imm && is_none(mut)) { - mut = some(auto_unbox.t); + if (mt.mut != ast::imm) { + if (is_none(mut)) { mut = some(auto_unbox.t); } + if (depth == 0) { mut_fld = true; } } } } @@ -415,8 +455,6 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) if (!is_none(mut)) { mut_in_box = true; } else if (auto_unbox.mut) { mut = some(base_t); } } - if (auto_unbox.done && !is_none(mut)) { - } ex = base; } case (ast::expr_unary(?op, ?base, _)) { @@ -424,8 +462,9 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) auto base_t = ty::expr_ty(*cx.tcx, base); alt (ty::struct(*cx.tcx, base_t)) { case (ty::ty_box(?mt)) { - if (mt.mut != ast::imm && is_none(mut)) { - mut = some(base_t); + if (mt.mut != ast::imm) { + if (is_none(mut)) { mut = some(base_t); } + if (depth == 0) { mut_fld = true; } } if (!is_none(mut)) { mut_in_box = true; @@ -439,16 +478,23 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) } case (_) { break; } } + depth += 1; } if (autoderef) { auto ex_t = ty::expr_ty(*cx.tcx, ex); auto auto_unbox = maybe_auto_unbox(cx, ex_t); if (auto_unbox.done) { if (!is_none(mut)) { mut_in_box = true; } - else if (auto_unbox.mut) { mut = some(ex_t); } + else if (auto_unbox.mut) { + mut = some(ex_t); + if (depth == 0) { mut_fld = true; } + } } } - ret rec(ex = ex, inner_mut = mut, mut_in_box = mut_in_box); + ret rec(ex = ex, + inner_mut = mut, + mut_in_box = mut_in_box, + mut_field = mut_fld); } fn maybe_auto_unbox(&ctx cx, &ty::t t) diff --git a/src/test/compile-fail/unsafe-mutable-alias.rs b/src/test/compile-fail/unsafe-mutable-alias.rs new file mode 100644 index 00000000000..b6968056b12 --- /dev/null +++ b/src/test/compile-fail/unsafe-mutable-alias.rs @@ -0,0 +1,12 @@ +// xfail-stage0 +// error-pattern:mutable alias to a variable that roots another alias + +fn f(&int a, &mutable int b) -> int { + b += 1; + ret a + b; +} + +fn main() { + auto i = 4; + log f(i, i); +}