about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/comp/front/ast.rs4
-rw-r--r--src/comp/middle/alias.rs543
-rw-r--r--src/comp/middle/resolve.rs2
-rw-r--r--src/test/compile-fail/unsafe-alias-2.rs15
-rw-r--r--src/test/compile-fail/unsafe-alias.rs10
-rw-r--r--src/test/compile-fail/unsafe-alt.rs3
6 files changed, 423 insertions, 154 deletions
diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs
index 4f83ee7fa8d..fce15a556c6 100644
--- a/src/comp/front/ast.rs
+++ b/src/comp/front/ast.rs
@@ -11,6 +11,10 @@ type ident = str;
 type path_ = rec(vec[ident] idents, vec[@ty] types);
 type path = spanned[path_];
 
+fn path_name(&path p) -> str {
+    ret str::connect(p.node.idents, "::");
+}
+
 type crate_num = int;
 const crate_num local_crate = 0;
 type def_num = int;
diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs
index b433799ac36..fa978ef53c0 100644
--- a/src/comp/middle/alias.rs
+++ b/src/comp/middle/alias.rs
@@ -1,209 +1,388 @@
 import front::ast;
 import front::ast::ident;
-import front::ast::def_id;
+import front::ast::def_num;
+import util::common::span;
+import visit::vt;
 import std::vec;
 import std::str;
 import std::option;
 import std::option::some;
 import std::option::none;
+import std::option::is_none;
 
-tag deref_t {
-    field(ident);
-    index;
-    unbox;
+tag valid {
+    valid;
+    overwritten(span, ast::path);
+    val_taken(span, ast::path);
 }
-type deref = rec(bool mut, deref_t t);
 
-type ctx = @rec(@ty::ctxt tcx,
-                resolve::def_map dm,
-                // The current blacklisted (non-assignable) locals
-                mutable vec[vec[def_id]] bl,
-                // A stack of blacklists for outer function scopes
-                mutable vec[vec[vec[def_id]]] blstack);
+type restrict = @rec(vec[def_num] root_vars,
+                     def_num block_defnum,
+                     vec[def_num] bindings,
+                     vec[ty::t] tys,
+                     mutable valid ok);
+
+type scope = vec[restrict];
+type ctx = rec(@ty::ctxt tcx,
+               resolve::def_map dm);
 
 fn check_crate(@ty::ctxt tcx, resolve::def_map dm, &@ast::crate crate) {
-    auto cx = @rec(tcx = tcx,
-                   dm = dm,
-                   mutable bl = vec::empty[vec[def_id]](),
-                   mutable blstack = vec::empty[vec[vec[def_id]]]());
-    auto v = rec(visit_item_pre = bind enter_item(cx, _),
-                 visit_item_post = bind leave_item(cx, _),
-                 visit_method_pre = bind enter_method(cx, _),
-                 visit_method_post = bind leave_method(cx, _),
-                 visit_expr_pre = bind check_expr(cx, _),
-                 visit_expr_post = bind leave_expr(cx, _)
-                 with walk::default_visitor());
-    walk::walk_crate(v, *crate);
-}
-
-fn enter_item(ctx cx, &@ast::item it) {
-    alt (it.node) {
-        case (ast::item_fn(_, _, _, _, _)) {
-            vec::push(cx.blstack, cx.bl);
-            cx.bl = [];
-        }
-        case (_) {}
-    }
-}
-fn leave_item(ctx cx, &@ast::item it) {
-    alt (it.node) {
-        case (ast::item_fn(_, _, _, _, _)) {
-            cx.bl = vec::pop(cx.blstack);
-        }
-        case (_) {}
-    }
+    auto cx = @rec(tcx = tcx, dm = dm);
+    auto v = @rec(visit_fn = visit_fn,
+                  visit_expr = bind visit_expr(cx, _, _, _)
+                  with *visit::default_visitor[scope]());
+    visit::visit_crate(*crate, [], visit::vtor(v));
 }
 
-fn enter_method(ctx cx, &@ast::method mt) {
-    vec::push(cx.blstack, cx.bl);
-    cx.bl = [];
-}
-fn leave_method(ctx cx, &@ast::method mt) {
-    cx.bl = vec::pop(cx.blstack);
+fn visit_fn(&ast::_fn f, &span sp, &ident name, &ast::def_id d_id,
+            &ast::ann a, &scope sc, &vt[scope] v) {
+    visit::visit_fn_decl(f.decl, sc, v);
+    vt(v).visit_block(f.body, [], v);
 }
 
-fn check_expr(ctx cx, &@ast::expr ex) {
+fn visit_expr(&@ctx cx, &@ast::expr ex, &scope sc, &vt[scope] v) {
+    auto handled = false;
     alt (ex.node) {
         case (ast::expr_call(?f, ?args, _)) {
-            auto fty = ty::expr_ty(*cx.tcx, f);
-            auto argtys = alt (ty::struct(*cx.tcx, fty)) {
-                case (ty::ty_fn(_, ?args, _, _)) { args }
-                case (ty::ty_native_fn(_, ?args, _)) { args }
-            };
-            auto i = 0u;
-            let vec[def_id] listed = [];
-            for (ty::arg argty in argtys) {
-                if (argty.mode != ty::mo_val) {
-                    alt (check_rooted(cx, args.(i), false)) {
-                        case (some(?did)) {
-                            vec::push(listed, did);
-                        }
-                        case (_) {}
-                    }
-                }
-                i += 1u;
-            }
-            // FIXME when mutable aliases can be distinguished, go over the
-            // args again and ensure that we're not passing a blacklisted
-            // variable by mutable alias (using 'listed' and the context
-            // blacklist).
+            check_call(*cx, f, args, sc);
+        }
+        case (ast::expr_alt(?input, ?arms, _)) {
+            check_alt(*cx, input, arms, sc, v);
+            handled = true;
         }
         case (ast::expr_put(?val, _)) {
             alt (val) {
-                case (some(?ex)) { check_rooted(cx, ex, false); }
+                case (some(?ex)) {
+                    auto root = expr_root(*cx, ex, false);
+                    if (!is_none(root.inner_mut)) {
+                        cx.tcx.sess.span_err
+                            (ex.span,
+                             "result of put must be immutably rooted");
+                    }
+                    visit_expr(cx, ex, sc, v);
+                }
                 case (_) {}
             }
+            handled = true;
+        }
+        case (ast::expr_for_each(?decl, ?call, ?block, _)) {
+            check_for_each(*cx, decl, call, block, sc, v);
+            handled = true;
         }
-        case (ast::expr_alt(?input, _, _)) {
-            vec::push(cx.bl, alt (check_rooted(cx, input, true)) {
-                case (some(?did)) { [did] }
-                case (_) { vec::empty[def_id]() }
-            });
+        case (ast::expr_for(?decl, ?seq, ?block, _)) {
+            check_for(*cx, decl, seq, block, sc, v);
+            handled = true;
+        }
+
+        case (ast::expr_path(?pt, ?ann)) {
+            check_var(*cx, ex, pt, ann, false, sc);
+        }
+        case (ast::expr_move(?dest, ?src, _)) {
+            check_assign(cx, dest, src, sc, v);
+            handled = true;
+        }
+        case (ast::expr_assign(?dest, ?src, _)) {
+            check_assign(cx, dest, src, sc, v);
+            handled = true;
+        }
+        case (ast::expr_assign_op(_, ?dest, ?src, _)) {
+            check_assign(cx, dest, src, sc, v);
+            handled = true;
         }
 
-        case (ast::expr_move(?dest, _, _)) { check_assign(cx, dest); }
-        case (ast::expr_assign(?dest, _, _)) { check_assign(cx, dest); }
-        case (ast::expr_assign_op(_, ?dest, _, _)) { check_assign(cx, dest); }
         case (_) {}
     }
+    if (!handled) { visit::visit_expr(ex, sc, v); }
 }
 
-fn leave_expr(ctx cx, &@ast::expr ex) {
-    alt (ex.node) {
-        case (ast::expr_alt(_, _, _)) { vec::pop(cx.bl); }
-        case (_) {}
+fn check_call(&ctx cx, &@ast::expr f, &vec[@ast::expr] args, &scope sc)
+    -> rec(vec[def_num] root_vars, vec[ty::t] unsafe_ts) {
+    auto fty = ty::expr_ty(*cx.tcx, f);
+    auto arg_ts = alt (ty::struct(*cx.tcx, fty)) {
+        case (ty::ty_fn(_, ?args, _, _)) { args }
+        case (ty::ty_native_fn(_, ?args, _)) { args }
+    };
+
+    auto i = 0u;
+    let vec[def_num] roots = [];
+    let vec[ty::t] unsafe_ts = [];
+    let vec[uint] unsafe_t_offsets = [];
+    for (ty::arg arg_t in arg_ts) {
+        if (arg_t.mode != ty::mo_val) {
+            auto root = expr_root(cx, args.(i), false);
+            alt (path_def_id(cx, root.ex)) {
+                case (some(?did)) { vec::push(roots, did._1); }
+                case (_) {}
+            }
+            alt (root.inner_mut) {
+                case (some(?t)) {
+                    vec::push(unsafe_ts, t);
+                    vec::push(unsafe_t_offsets, i);
+                }
+                case (_) {}
+            }
+        }
+        i += 1u;
     }
+
+    if (vec::len(unsafe_ts) > 0u) {
+        alt (f.node) {
+            case (ast::expr_path(_, ?ann)) {
+                if (def_is_local(cx.dm.get(ann.id))) {
+                    cx.tcx.sess.span_err
+                        (f.span, #fmt("function may alias with argument \
+                         %u, which is not immutably rooted",
+                         unsafe_t_offsets.(0)));
+                }
+            }
+            case (_) {}
+        }
+    }
+    auto j = 0u;
+    for (ty::t unsafe in unsafe_ts) {
+        auto offset = unsafe_t_offsets.(j);
+        j += 1u;
+        auto i = 0u;
+        for (ty::arg arg_t in arg_ts) {
+            if (i != offset &&
+                // FIXME false should be replace with mutability of alias
+                ty_can_unsafely_include(cx, unsafe, arg_t.ty, false)) {
+                cx.tcx.sess.span_err
+                    (args.(i).span, #fmt("argument %u may alias with \
+                     argument %u, which is not immutably rooted", i, offset));
+            }
+            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).
+
+    ret rec(root_vars = roots, unsafe_ts = unsafe_ts);
 }
 
-fn check_assign(&ctx cx, &@ast::expr ex) {
-    alt (ex.node) {
-        case (ast::expr_path(?pt, ?ann)) {
-            auto did = ast::def_id_of_def(cx.dm.get(ann.id));
-            for (vec[def_id] layer in cx.bl) {
-                for (def_id black in layer) {
-                    if (did == black) {
-                        cx.tcx.sess.span_err
-                            (ex.span, str::connect(pt.node.idents, "::") +
-                             " is being aliased and may not be assigned to");
-                    }
+fn check_alt(&ctx cx, &@ast::expr input, &vec[ast::arm] arms,
+             &scope sc, &vt[scope] v) {
+    visit::visit_expr(input, sc, v);
+    auto root = expr_root(cx, input, true);
+    auto roots = alt (path_def_id(cx, root.ex)) {
+        case (some(?did)) { [did._1] }
+        case (_) { [] }
+    };
+    let vec[ty::t] forbidden_tp = alt (root.inner_mut) {
+        case (some(?t)) { [t] }
+        case (_) { [] }
+    };
+
+    for (ast::arm a in arms) {
+        auto dnums = arm_defnums(a);
+        auto new_sc = sc;
+        if (vec::len(dnums) > 0u) {
+            vec::push(new_sc, @rec(root_vars=roots,
+                                   block_defnum=dnums.(0),
+                                   bindings=dnums,
+                                   tys=forbidden_tp,
+                                   mutable ok=valid));
+        }
+        visit::visit_arm(a, new_sc, v);
+    }
+}
+
+fn arm_defnums(&ast::arm arm) -> vec[def_num] {
+    auto dnums = [];
+    fn walk_pat(&mutable vec[def_num] found, &@ast::pat p) {
+        alt (p.node) {
+            case (ast::pat_bind(_, ?did, _)) {
+                vec::push(found, did._1);
+            }
+            case (ast::pat_tag(_, ?children, _)) {
+                for (@ast::pat child in children) {
+                    walk_pat(found, child);
                 }
             }
+            case (_) {}
         }
-        case (_) {}
     }
+    walk_pat(dnums, arm.pat);
+    ret dnums;
 }
 
-fn check_rooted(&ctx cx, &@ast::expr ex, bool autoderef)
-    -> option::t[def_id] {
-    auto root = expr_root(cx, ex, autoderef);
-    if (has_unsafe_box(root.ds)) {
-        cx.tcx.sess.span_err
-            (ex.span, "can not create alias to improperly anchored value");
+fn check_for_each(&ctx cx, &@ast::decl decl, &@ast::expr call,
+                  &ast::block block, &scope sc, &vt[scope] v) {
+    visit::visit_expr(call, sc, v);
+    alt (call.node) {
+        case (ast::expr_call(?f, ?args, _)) {
+            auto data = check_call(cx, f, args, sc);
+            auto defnum = alt (decl.node) {
+                case (ast::decl_local(?l)) { l.id._1 }
+            };
+            
+            auto new_sc = @rec(root_vars=data.root_vars,
+                               block_defnum=defnum,
+                               bindings=[defnum],
+                               tys=data.unsafe_ts,
+                               mutable ok=valid);
+            visit::visit_block(block, sc + [new_sc], v);
+        }
     }
-    alt (root.ex.node) {
-        case (ast::expr_path(_, ?ann)) {
-            ret some(ast::def_id_of_def(cx.dm.get(ann.id)));
+}
+
+fn check_for(&ctx cx, &@ast::decl decl, &@ast::expr seq,
+             &ast::block block, &scope sc, &vt[scope] v) {
+    visit::visit_expr(seq, sc, v);
+    auto defnum = alt (decl.node) {
+        case (ast::decl_local(?l)) { l.id._1 }
+    };
+
+    auto root = expr_root(cx, seq, false);
+    auto root_def = alt (path_def_id(cx, root.ex)) {
+        case (some(?did)) { [did._1] }
+        case (_) { [] }
+    };
+    auto unsafe = alt (root.inner_mut) {
+        case (some(?t)) { [t] }
+        case (_) { [] }
+    };
+    // If this is a mutable vector, don't allow it to be touched.
+    auto seq_t = ty::expr_ty(*cx.tcx, seq);
+    alt (ty::struct(*cx.tcx, seq_t)) {
+        case (ty::ty_vec(?mt)) {
+            if (mt.mut != ast::imm) { unsafe = [seq_t]; }
         }
-        case (_) {
-            ret none[def_id];
+        case (ty::ty_str) {}
+    }
+
+    auto new_sc = @rec(root_vars=root_def,
+                       block_defnum=defnum,
+                       bindings=[defnum],
+                       tys=unsafe,
+                       mutable ok=valid);
+    visit::visit_block(block, sc + [new_sc], v);
+}
+
+fn check_var(&ctx cx, &@ast::expr ex, &ast::path p, ast::ann ann, bool assign,
+             &scope sc) {
+    auto def = cx.dm.get(ann.id);
+    if (!def_is_local(def)) { ret; }
+    auto my_defnum = ast::def_id_of_def(def)._1;
+    auto var_t = ty::expr_ty(*cx.tcx, ex);
+    for (restrict r in sc) {
+        // excludes variables introduced since the alias was made
+        if (my_defnum < r.block_defnum) {
+            for (ty::t t in r.tys) {
+                if (ty_can_unsafely_include(cx, t, var_t, assign)) {
+                    r.ok = val_taken(ex.span, p);
+                }
+            }
+        } else if (r.ok != valid && vec::member(my_defnum, r.bindings)) {
+            fail_alias(cx, r.ok, p);
         }
     }
 }
 
-fn expr_root(&ctx cx, @ast::expr ex, bool autoderef)
-    -> rec(@ast::expr ex, vec[deref] ds) {
-    let vec[deref] ds = [];
-    if (autoderef) {
-        auto auto_unbox = maybe_auto_unbox(cx, ex);
-        if (auto_unbox.done) {
-            vec::push(ds, rec(mut=auto_unbox.mut, t=unbox));
+fn fail_alias(&ctx cx, valid issue, &ast::path pt) {
+    auto base = " will invalidate alias " + ast::path_name(pt) +
+        ", which is still used";
+    alt (issue) {
+        case (overwritten(?sp, ?wpt)) {
+            cx.tcx.sess.span_err
+                (sp, "overwriting " + ast::path_name(wpt) + base);
+        }
+        case (val_taken(?sp, ?vpt)) {
+            cx.tcx.sess.span_err
+                (sp, "taking the value of " + ast::path_name(vpt) +
+                 base);
+        }
+    }
+}
+
+fn check_assign(&@ctx cx, &@ast::expr dest, &@ast::expr src,
+                &scope sc, &vt[scope] v) {
+    visit_expr(cx, src, sc, v);
+    alt (dest.node) {
+        case (ast::expr_path(?p, ?ann)) {
+            auto dnum = ast::def_id_of_def(cx.dm.get(ann.id))._1;
+            auto var_t = ty::expr_ty(*cx.tcx, dest);
+            for (restrict r in sc) {
+                if (vec::member(dnum, r.root_vars)) {
+                    r.ok = overwritten(dest.span, p);
+                }
+            }
+            check_var(*cx, dest, p, ann, true, sc);
+        }
+        case (_) {
+            visit_expr(cx, dest, sc, v);
         }
     }
+}
+
+fn expr_root(&ctx cx, @ast::expr ex, bool autoderef)
+    -> rec(@ast::expr ex, option::t[ty::t] inner_mut, bool mut_in_box) {
+    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;
     while (true) {
         alt ({ex.node}) {
             case (ast::expr_field(?base, ?ident, _)) {
-                auto auto_unbox = maybe_auto_unbox(cx, base);
-                alt (auto_unbox.t) {
+                auto base_t = ty::expr_ty(*cx.tcx, base);
+                auto auto_unbox = maybe_auto_unbox(cx, base_t);
+                alt (ty::struct(*cx.tcx, auto_unbox.t)) {
                     case (ty::ty_tup(?fields)) {
                         auto fnm = ty::field_num(cx.tcx.sess, ex.span, ident);
-                        auto mt = fields.(fnm).mut != ast::imm;
-                        vec::push(ds, rec(mut=mt, t=field(ident)));
+                        if (fields.(fnm).mut != ast::imm && is_none(mut)) {
+                            mut = some(auto_unbox.t);
+                        }
                     }
                     case (ty::ty_rec(?fields)) {
                         for (ty::field fld in fields) {
                             if (str::eq(ident, fld.ident)) {
-                                auto mt = fld.mt.mut != ast::imm;
-                                vec::push(ds, rec(mut=mt, t=field(ident)));
+                                if (fld.mt.mut != ast::imm && is_none(mut)) {
+                                    mut = some(auto_unbox.t);
+                                }
                                 break;
                             }
                         }
                     }
-                    case (ty::ty_obj(_)) {
-                        vec::push(ds, rec(mut=false, t=field(ident)));
-                    }
+                    case (ty::ty_obj(_)) {}
                 }
                 if (auto_unbox.done) {
-                    vec::push(ds, rec(mut=auto_unbox.mut, t=unbox));
+                    if (!is_none(mut)) { mut_in_box = true; }
+                    else if (auto_unbox.mut) { mut = some(base_t); }
                 }
                 ex = base;
             }
             case (ast::expr_index(?base, _, _)) {
-                auto auto_unbox = maybe_auto_unbox(cx, base);
-                alt (auto_unbox.t) {
+                auto base_t = ty::expr_ty(*cx.tcx, base);
+                auto auto_unbox = maybe_auto_unbox(cx, base_t);
+                alt (ty::struct(*cx.tcx, auto_unbox.t)) {
                     case (ty::ty_vec(?mt)) {
-                        vec::push(ds, rec(mut=mt.mut != ast::imm, t=index));
+                        if (mt.mut != ast::imm && is_none(mut)) {
+                            mut = some(auto_unbox.t);
+                        }
                     }
                 }
                 if (auto_unbox.done) {
-                    vec::push(ds, rec(mut=auto_unbox.mut, t=unbox));
+                    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, _)) {
                 if (op == ast::deref) {
-                    alt (ty::struct(*cx.tcx, ty::expr_ty(*cx.tcx, base))) {
+                    auto base_t = ty::expr_ty(*cx.tcx, base);
+                    alt (ty::struct(*cx.tcx, base_t)) {
                         case (ty::ty_box(?mt)) {
-                            vec::push(ds, rec(mut=mt.mut!=ast::imm, t=unbox));
+                            if (mt.mut != ast::imm && is_none(mut)) {
+                                mut = some(base_t);
+                            }
+                            if (!is_none(mut)) {
+                                mut_in_box = true;
+                            }
                         }
                     }
                     ex = base;
@@ -214,34 +393,100 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef)
             case (_) { break; }
         }
     }
-    vec::reverse(ds);
-    ret rec(ex = ex, ds = ds);
+    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); }
+        }
+    }
+    ret rec(ex = ex, inner_mut = mut, mut_in_box = mut_in_box);
 }
 
-fn maybe_auto_unbox(&ctx cx, &@ast::expr ex)
-    -> rec(ty::sty t, bool done, bool mut) {
-    auto tp = ty::struct(*cx.tcx, ty::expr_ty(*cx.tcx, ex));
-    alt (tp) {
+fn maybe_auto_unbox(&ctx cx, &ty::t t)
+    -> rec(ty::t t, bool done, bool mut) {
+    alt (ty::struct(*cx.tcx, t)) {
         case (ty::ty_box(?mt)) {
-            ret rec(t=ty::struct(*cx.tcx, mt.ty),
-                    done=true, mut=mt.mut != ast::imm);
+            ret rec(t=mt.ty, done=true, mut=mt.mut != ast::imm);
+        }
+        case (_) {
+            ret rec(t=t, done=false, mut=false);
         }
-        case (_) { ret rec(t=tp, done=false, mut=false); }
     }
 }
 
-fn has_unsafe_box(&vec[deref] ds) -> bool {
-    auto saw_mut = false;
-    for (deref d in ds) {
-        if (d.mut) { saw_mut = true; }
-        if (d.t == unbox) {
-            // Directly aliasing the content of a mutable box is never okay,
-            // and any box living under mutable connection may be severed from
-            // its root and freed.
-            if (saw_mut) { ret true; }
+fn path_def_id(&ctx cx, &@ast::expr ex) -> option::t[ast::def_id] {
+    alt (ex.node) {
+        case (ast::expr_path(_, ?ann)) {
+            ret some(ast::def_id_of_def(cx.dm.get(ann.id)));
+        }
+        case (_) {
+            ret none;
         }
     }
-    ret false;
+}
+
+fn ty_can_unsafely_include(&ctx cx, ty::t needle, ty::t haystack, bool mut)
+    -> bool {
+    fn get_mut(bool cur, &ty::mt mt) -> bool {
+        ret cur || mt.mut != ast::imm;
+    }
+    fn helper(&ty::ctxt tcx, ty::t needle, ty::t haystack, bool mut) -> bool {
+        if (needle == haystack) { ret true; }
+        alt (ty::struct(tcx, haystack)) {
+            case (ty::ty_tag(_, ?ts)) {
+                for (ty::t t in ts) {
+                    if (helper(tcx, needle, t, mut)) { ret true; }
+                }
+                ret false;
+            }
+            case (ty::ty_box(?mt)) {
+                ret helper(tcx, needle, mt.ty, get_mut(mut, mt));
+            }
+            case (ty::ty_vec(?mt)) {
+                ret helper(tcx, needle, mt.ty, get_mut(mut, mt));
+            }
+            case (ty::ty_ptr(?mt)) {
+                ret helper(tcx, needle, mt.ty, get_mut(mut, mt));
+            }
+            case (ty::ty_tup(?mts)) {
+                for (ty::mt mt in mts) {
+                    if (helper(tcx, needle, mt.ty, get_mut(mut, mt))) {
+                        ret true;
+                    }
+                }
+                ret false;
+            }
+            case (ty::ty_rec(?fields)) {
+                for (ty::field f in fields) {
+                    if (helper(tcx, needle, f.mt.ty, get_mut(mut, f.mt))) {
+                        ret true;
+                    }
+                }
+                ret false;
+            }
+            // These may contain anything.
+            case (ty::ty_fn(_, _, _, _)) { ret true; }
+            case (ty::ty_obj(_)) { ret true; }
+            // A type param may include everything, but can only be treated as
+            // opaque downstream, and is thus safe unless we saw mutable
+            // fields, in which case the whole thing can be overwritten.
+            case (ty::ty_param(_)) { ret mut; }
+            case (_) { ret false; }
+        }
+    }
+    ret helper(*cx.tcx, needle, haystack, mut);
+}
+
+fn def_is_local(&ast::def d) -> bool {
+    ret alt (d) {
+        case (ast::def_local(_)) { true }
+        case (ast::def_arg(_)) { true }
+        case (ast::def_obj_field(_)) { true }
+        case (ast::def_binding(_)) { true }
+        case (_) { false }
+    };
 }
 
 // Local Variables:
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 8ec4dfe6297..1bd7307a5ab 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -307,7 +307,7 @@ fn resolve_names(&@env e, &ast::crate c) {
                     }
                     case (_) {
                         e.sess.span_err(p.span, "not a tag variant: " +
-                                        str::connect(p.node.idents, "::"));
+                                        ast::path_name(p));
                     }
                 }
                 for (@ast::pat child in children) {
diff --git a/src/test/compile-fail/unsafe-alias-2.rs b/src/test/compile-fail/unsafe-alias-2.rs
new file mode 100644
index 00000000000..4a9551a9130
--- /dev/null
+++ b/src/test/compile-fail/unsafe-alias-2.rs
@@ -0,0 +1,15 @@
+// error-pattern:invalidate alias x
+
+fn whoknows(@mutable int x) {
+    *x = 10;
+}
+
+fn main() {
+    auto box = @mutable 1;
+    alt (*box) {
+        case (?x) {
+            whoknows(box);
+            log_err x;
+        }
+    }
+}
diff --git a/src/test/compile-fail/unsafe-alias.rs b/src/test/compile-fail/unsafe-alias.rs
index e4ff5f86da2..a81b490266d 100644
--- a/src/test/compile-fail/unsafe-alias.rs
+++ b/src/test/compile-fail/unsafe-alias.rs
@@ -1,10 +1,14 @@
-// error-pattern:can not create alias
+// error-pattern:may alias with argument
 
-fn foo(&int x) {
+fn foo(&int x, fn() f) {
     log x;
 }
 
+fn whoknows(@mutable int x) {
+    *x = 10;
+}
+
 fn main() {
     auto box = @mutable 1;
-    foo(*box);
+    foo(*box, bind whoknows(box));
 }
diff --git a/src/test/compile-fail/unsafe-alt.rs b/src/test/compile-fail/unsafe-alt.rs
index a0342d87c86..4144e3751db 100644
--- a/src/test/compile-fail/unsafe-alt.rs
+++ b/src/test/compile-fail/unsafe-alt.rs
@@ -1,4 +1,4 @@
-// error-pattern:x is being aliased
+// error-pattern:invalidate alias i
 
 tag foo {
     left(int);
@@ -10,6 +10,7 @@ fn main() {
     alt (x) {
         case (left(?i)) {
             x = right(false);
+            log i;
         }
         case (_) {}
     }