Handle mutable references in alias analysis

This commit is contained in:
Marijn Haverbeke 2011-06-10 16:38:13 +02:00
parent f28796ed99
commit e25e05539e
2 changed files with 90 additions and 32 deletions

View file

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

View file

@ -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);
}