]> git.lizzy.rs Git - rust.git/commitdiff
Revert "remove alias analysis and replace with borrowck"
authorBrian Anderson <banderson@mozilla.com>
Fri, 8 Jun 2012 02:42:22 +0000 (19:42 -0700)
committerBrian Anderson <banderson@mozilla.com>
Fri, 8 Jun 2012 02:42:27 +0000 (19:42 -0700)
18s perf regression compiling rustc with opts

This reverts commit 7f6ee0ce0df8af4c21b065cb49b95079ae643f77.

51 files changed:
doc/tutorial.md
src/rustc/driver/driver.rs
src/rustc/driver/session.rs
src/rustc/metadata/common.rs
src/rustc/middle/alias.rs [new file with mode: 0644]
src/rustc/middle/astencode.rs
src/rustc/middle/borrowck.rs
src/rustc/middle/trans/alt.rs
src/rustc/middle/trans/base.rs
src/rustc/middle/trans/reflect.rs
src/rustc/rustc.rc
src/test/compile-fail/borrowck-assign-comp-idx.rs
src/test/compile-fail/borrowck-assign-comp.rs
src/test/compile-fail/borrowck-lend-args.rs
src/test/compile-fail/borrowck-lend-flow.rs
src/test/compile-fail/borrowck-loan-blocks-move-cc.rs
src/test/compile-fail/borrowck-loan-blocks-move.rs
src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs
src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
src/test/compile-fail/borrowck-loan-rcvr.rs
src/test/compile-fail/borrowck-loan-vec-content.rs
src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs
src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs [deleted file]
src/test/compile-fail/borrowck-pat-enum-in-box.rs
src/test/compile-fail/borrowck-pat-enum.rs
src/test/compile-fail/borrowck-pat-reassign-binding.rs
src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs
src/test/compile-fail/borrowck-pure-scope-in-call.rs
src/test/compile-fail/borrowck-unchecked-with-borrow.rs
src/test/compile-fail/borrowck-uniq-via-box.rs
src/test/compile-fail/borrowck-uniq-via-lend.rs
src/test/compile-fail/borrowck-uniq-via-ref.rs
src/test/compile-fail/issue-511.rs
src/test/compile-fail/unsafe-alias-2.rs [new file with mode: 0644]
src/test/compile-fail/unsafe-alias.rs [new file with mode: 0644]
src/test/compile-fail/unsafe-alt.rs [new file with mode: 0644]
src/test/compile-fail/unsafe-mutable-alias.rs [new file with mode: 0644]
src/test/run-pass/alt-implicit-copy-unique.rs
src/test/run-pass/alt-implicit-copy.rs
src/test/run-pass/borrowck-mut-vec-as-imm-slice.rs
src/test/run-pass/borrowck-pat-reassign-no-binding.rs
src/test/run-pass/borrowck-preserve-box-in-arm-not-taken.rs
src/test/run-pass/borrowck-preserve-box-in-discr.rs
src/test/run-pass/borrowck-preserve-box-in-field.rs
src/test/run-pass/borrowck-preserve-box-in-pat.rs
src/test/run-pass/borrowck-preserve-box-in-uniq.rs
src/test/run-pass/borrowck-preserve-box-sometimes-needed.rs
src/test/run-pass/borrowck-preserve-box.rs
src/test/run-pass/borrowck-preserve-cond-box.rs
src/test/run-pass/borrowck-preserve-expl-deref.rs
src/test/run-pass/reflect-visit-type.rs

index 804cf39d640aebe92800068d234e1e82c810201e..c7b94c8e1550e591c2c750555c2a812c8f0af959 100644 (file)
@@ -1382,7 +1382,83 @@ gets access to them.
 
 ## Safe references
 
-*This system has recently changed.  An explanantion is forthcoming.*
+There is one catch with this approach: sometimes the compiler can
+*not* statically guarantee that the argument value at the caller side
+will survive to the end of the call. Another argument might indirectly
+refer to it and be used to overwrite it, or a closure might assign a
+new value to it.
+
+Fortunately, Rust tasks are single-threaded worlds, which share no
+data with other tasks, and most data is immutable. This allows most
+argument-passing situations to be proved safe without further
+difficulty.
+
+Take the following program:
+
+~~~~
+# fn get_really_big_record() -> int { 1 }
+# fn myfunc(a: int) {}
+fn main() {
+    let x = get_really_big_record();
+    myfunc(x);
+}
+~~~~
+
+Here we know for sure that no one else has access to the `x` variable
+in `main`, so we're good. But the call could also look like this:
+
+~~~~
+# fn myfunc(a: int, b: fn()) {}
+# fn get_another_record() -> int { 1 }
+# let mut x = 1;
+myfunc(x, {|| x = get_another_record(); });
+~~~~
+
+Now, if `myfunc` first calls its second argument and then accesses its
+first argument, it will see a different value from the one that was
+passed to it.
+
+In such a case, the compiler will insert an implicit copy of `x`,
+*except* if `x` contains something mutable, in which case a copy would
+result in code that behaves differently. If copying `x` might be
+expensive (for example, if it holds a vector), the compiler will emit
+a warning.
+
+There are even more tricky cases, in which the Rust compiler is forced
+to pessimistically assume a value will get mutated, even though it is
+not sure.
+
+~~~~
+fn for_each(v: [mut @int], iter: fn(@int)) {
+   for v.each {|elt| iter(elt); }
+}
+~~~~
+
+For all this function knows, calling `iter` (which is a closure that
+might have access to the vector that's passed as `v`) could cause the
+elements in the vector to be mutated, with the effect that it can not
+guarantee that the boxes will live for the duration of the call. So it
+has to copy them. In this case, this will happen implicitly (bumping a
+reference count is considered cheap enough to not warn about it).
+
+## The copy operator
+
+If the `for_each` function given above were to take a vector of
+`{mut a: int}` instead of `@int`, it would not be able to
+implicitly copy, since if the `iter` function changes a copy of a
+mutable record, the changes won't be visible in the record itself. If
+we *do* want to allow copies there, we have to explicitly allow it
+with the `copy` operator:
+
+~~~~
+type mutrec = {mut x: int};
+fn for_each(v: [mut mutrec], iter: fn(mutrec)) {
+   for v.each {|elt| iter(copy elt); }
+}
+~~~~
+
+Adding a `copy` operator is also the way to muffle warnings about
+implicit copies.
 
 ## Other uses of safe references
 
index 769f3c40e259e8f440601a1d77ed9d8da1c59d20..672a950f42b1fe6a6c73d1b6161475e67cca24bb 100644 (file)
@@ -204,6 +204,9 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
     let (root_map, mutbl_map) = time(
         time_passes, "borrow checking",
         bind middle::borrowck::check_crate(ty_cx, method_map, crate));
+    let (copy_map, _ref_map) =
+        time(time_passes, "alias checking",
+             bind middle::alias::check_crate(ty_cx, crate));
     time(time_passes, "kind checking",
          bind kind::check_crate(ty_cx, method_map, last_use_map, crate));
     time(time_passes, "lint checking",
@@ -213,7 +216,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
     let outputs = option::get(outputs);
 
     let maps = {mutbl_map: mutbl_map, root_map: root_map,
-                last_use_map: last_use_map,
+                copy_map: copy_map, last_use_map: last_use_map,
                 impl_map: impl_map, method_map: method_map,
                 vtable_map: vtable_map};
 
@@ -445,6 +448,14 @@ fn build_session_options(match: getopts::match,
     let sysroot_opt = getopts::opt_maybe_str(match, "sysroot");
     let target_opt = getopts::opt_maybe_str(match, "target");
     let save_temps = getopts::opt_present(match, "save-temps");
+    let borrowck = alt getopts::opt_maybe_str(match, "borrowck") {
+      none { 0u }
+      some("warn") { 1u }
+      some("err") { 2u }
+      some(_) {
+        early_error(demitter, "borrowck may be warn or err")
+      }
+    };
     alt output_type {
       // unless we're emitting huamn-readable assembly, omit comments.
       link::output_type_llvm_assembly | link::output_type_assembly {}
@@ -493,7 +504,8 @@ fn build_session_options(match: getopts::match,
           test: test,
           parse_only: parse_only,
           no_trans: no_trans,
-          debugging_opts: debugging_opts};
+          debugging_opts: debugging_opts,
+          borrowck: borrowck};
     ret sopts;
 }
 
@@ -570,7 +582,8 @@ fn opts() -> [getopts::opt] {
          optmulti("Z"),
 
          optmulti("cfg"), optflag("test"),
-         optflag("lib"), optflag("bin"), optflag("static"), optflag("gc")];
+         optflag("lib"), optflag("bin"), optflag("static"), optflag("gc"),
+         optopt("borrowck")];
 }
 
 type output_filenames = @{out_filename: str, obj_filename:str};
index 07f42a0d26945c19f6f67609de29a4595db92b0e..97e2d0ec4db730d7b589363da1dcf38d68f5eb57 100644 (file)
@@ -66,6 +66,9 @@ fn debugging_opts_map() -> [(str, str, uint)] {
      no_trans: bool,
 
      debugging_opts: uint,
+
+     // temporary hack: 0=off,1=warn,2=err --> if 2, alias is disabled
+     borrowck: uint,
     };
 
 type crate_metadata = {name: str, data: [u8]};
@@ -178,7 +181,8 @@ fn basic_options() -> @options {
         test: false,
         parse_only: false,
         no_trans: false,
-        debugging_opts: 0u
+        debugging_opts: 0u,
+        borrowck: 0u,
     }
 }
 
index ba23dd24baa1bf450c62ab25905600cc0087e9ae..a754d4c11a2685feb79a32337fc5b854d96e8351 100644 (file)
@@ -116,6 +116,7 @@ enum astencode_tag { // Reserves 0x50 -- 0x6f
     tag_table_param_bounds,
     tag_table_inferred_modes,
     tag_table_mutbl,
+    tag_table_copy,
     tag_table_last_use,
     tag_table_spill,
     tag_table_method_map,
diff --git a/src/rustc/middle/alias.rs b/src/rustc/middle/alias.rs
new file mode 100644 (file)
index 0000000..1fb17d4
--- /dev/null
@@ -0,0 +1,847 @@
+import syntax::{ast, ast_util, ast_map};
+import ast_util::path_to_ident;
+import ast::{ident, fn_ident, node_id};
+import syntax::codemap::span;
+import syntax::visit;
+import visit::vt;
+import std::list;
+import std::map::hashmap;
+import core::unreachable;
+import option::is_none;
+import list::list;
+import driver::session::session;
+import pat_util::*;
+import util::ppaux::ty_to_str;
+
+// This is not an alias-analyser (though it would merit from becoming one, or
+// getting input from one, to be more precise). It is a pass that checks
+// whether aliases are used in a safe way.
+
+enum copied { not_allowed, copied, not_copied, }
+enum invalid_reason { overwritten, val_taken, }
+type invalid = {reason: invalid_reason,
+                node_id: node_id,
+                sp: span, path: @ast::path};
+
+enum unsafe_ty { contains(ty::t), mutbl_contains(ty::t), }
+
+type binding = @{node_id: node_id,
+                 span: span,
+                 root_var: option<node_id>,
+                 local_id: uint,
+                 unsafe_tys: [unsafe_ty],
+                 mut copied: copied};
+
+// FIXME it may be worthwhile to use a linked list of bindings instead
+type scope = {bs: [binding],
+              invalid: @mut @list<@invalid>};
+
+fn mk_binding(cx: ctx, id: node_id, span: span, root_var: option<node_id>,
+              unsafe_tys: [unsafe_ty]) -> binding {
+    alt root_var {
+      some(r_id) { cx.ref_map.insert(id, r_id); }
+      _ {}
+    }
+    ret @{node_id: id, span: span, root_var: root_var,
+          local_id: local_id_of_node(cx, id),
+          unsafe_tys: unsafe_tys,
+          mut copied: not_copied};
+}
+
+enum local_info { local(uint), }
+
+type copy_map = std::map::hashmap<node_id, ()>;
+type ref_map = std::map::hashmap<node_id, node_id>;
+
+type ctx = {tcx: ty::ctxt,
+            copy_map: copy_map,
+            ref_map: ref_map,
+            mut silent: bool};
+
+fn check_crate(tcx: ty::ctxt, crate: @ast::crate) -> (copy_map, ref_map) {
+    // Stores information about function arguments that's otherwise not easily
+    // available.
+    let cx = @{tcx: tcx,
+               copy_map: std::map::int_hash(),
+               ref_map: std::map::int_hash(),
+               mut silent: false};
+    let v = @{visit_fn: bind visit_fn(cx, _, _, _, _, _, _, _),
+              visit_expr: bind visit_expr(cx, _, _, _),
+              visit_block: bind visit_block(cx, _, _, _)
+              with *visit::default_visitor::<scope>()};
+    let sc = {bs: [], invalid: @mut @list::nil};
+    visit::visit_crate(*crate, sc, visit::mk_vt(v));
+    tcx.sess.abort_if_errors();
+    ret (cx.copy_map, cx.ref_map);
+}
+
+fn visit_fn(cx: @ctx, _fk: visit::fn_kind, decl: ast::fn_decl,
+            body: ast::blk, _sp: span,
+            id: ast::node_id, sc: scope, v: vt<scope>) {
+    visit::visit_fn_decl(decl, sc, v);
+    let fty = ty::node_id_to_type(cx.tcx, id);
+
+    // Blocks need to obey any restrictions from the enclosing scope, and may
+    // be called multiple times.
+    let proto = ty::ty_fn_proto(fty);
+    alt proto {
+      ast::proto_block | ast::proto_any {
+        check_loop(*cx, sc) {|| v.visit_block(body, sc, v);}
+      }
+      ast::proto_box | ast::proto_uniq | ast::proto_bare {
+        let sc = {bs: [], invalid: @mut @list::nil};
+        v.visit_block(body, sc, v);
+      }
+    }
+}
+
+fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
+    let mut handled = true;
+    alt ex.node {
+      ast::expr_call(f, args, _) {
+        check_call(cx, sc, f, args, v);
+        visit_expr(cx, f, sc, v);
+      }
+      ast::expr_alt(input, arms, _) { check_alt(*cx, input, arms, sc, v); }
+      ast::expr_path(pt) {
+        check_var(*cx, ex, pt, ex.id, false, sc);
+        handled = false;
+      }
+      ast::expr_swap(lhs, rhs) {
+        check_lval(cx, lhs, sc, v);
+        check_lval(cx, rhs, sc, v);
+        handled = false;
+      }
+      ast::expr_move(dest, src) {
+        visit_expr(cx, src, sc, v);
+        check_lval(cx, dest, sc, v);
+        check_lval(cx, src, sc, v);
+      }
+      ast::expr_assign(dest, src) | ast::expr_assign_op(_, dest, src) {
+        visit_expr(cx, src, sc, v);
+        check_lval(cx, dest, sc, v);
+      }
+      ast::expr_if(c, then, els) { check_if(c, then, els, sc, v); }
+      ast::expr_while(_, _) {
+        check_loop(*cx, sc) {|| visit::visit_expr(ex, sc, v); }
+      }
+      _ { handled = false; }
+    }
+    if !handled { visit::visit_expr(ex, sc, v); }
+}
+
+fn visit_block(cx: @ctx, b: ast::blk, sc: scope, v: vt<scope>) {
+    let sc = sc;
+    for b.node.stmts.each {|stmt|
+        alt stmt.node {
+          ast::stmt_decl(@{node: ast::decl_item(it), _}, _) {
+            v.visit_item(it, sc, v);
+          }
+          ast::stmt_decl(@{node: ast::decl_local(locs), _}, _) {
+            for locs.each {|loc|
+                alt loc.node.init {
+                  some(init) {
+                    if init.op == ast::init_move {
+                        check_lval(cx, init.expr, sc, v);
+                    } else {
+                        visit_expr(cx, init.expr, sc, v);
+                    }
+                  }
+                  none { }
+                }
+            }
+          }
+          ast::stmt_expr(ex, _) | ast::stmt_semi(ex, _) {
+            v.visit_expr(ex, sc, v);
+          }
+        }
+    }
+    visit::visit_expr_opt(b.node.expr, sc, v);
+}
+
+fn cant_copy(cx: ctx, b: binding) -> bool {
+
+    if cx.tcx.sess.opts.borrowck == 2u {
+        // borrowck is enabled. disable alias analysis.
+        ret false;
+    }
+
+    alt b.copied {
+      not_allowed { ret true; }
+      copied { ret false; }
+      not_copied {}
+    }
+    let ty = ty::node_id_to_type(cx.tcx, b.node_id);
+    if ty::type_allows_implicit_copy(cx.tcx, ty) {
+        b.copied = copied;
+        cx.copy_map.insert(b.node_id, ());
+        if copy_is_expensive(cx.tcx, ty) {
+            cx.tcx.sess.span_warn(b.span,
+                                  "inserting an implicit copy for type " +
+                                  util::ppaux::ty_to_str(cx.tcx, ty));
+        }
+        ret false;
+    } else { ret true; }
+}
+
+// FIXME this is a really awful hack
+fn local_id_for_args(cx: ctx, args: [@ast::expr]) -> uint {
+    for vec::each(args) {|arg|
+        alt arg.node {
+          ast::expr_fn_block(decl, _, _) | ast::expr_fn(_, decl, _, _) |
+          ast::expr_loop_body(@{node: ast::expr_fn_block(decl, _, _), _}) {
+            if decl.inputs.len() > 0u {
+                ret local_id_of_node(cx, decl.inputs[0].id);
+            }
+          }
+          _ {}
+        }
+    }
+    0xFFFFFFFFu
+}
+
+fn check_call(cx: @ctx, sc: scope, f: @ast::expr, args: [@ast::expr],
+              v: vt<scope>) {
+    let fty = ty::expr_ty(cx.tcx, f);
+    let arg_ts = ty::ty_fn_args(fty);
+    let mut mut_roots: [{arg: @ast::expr, node: node_id}] = [];
+    let mut bindings = [];
+    let mut blocks = [], loc_id = local_id_for_args(*cx, args);
+    vec::iter2(args, arg_ts) {|arg, arg_t|
+        let root = expr_root(*cx, arg, false);
+        alt ty::resolved_mode(cx.tcx, arg_t.mode) {
+          ast::by_mutbl_ref {
+            alt path_def(*cx, arg) {
+              some(def) {
+                let dnum = ast_util::def_id_of_def(def).node;
+                mut_roots += [{arg: arg, node: dnum}];
+              }
+              _ { }
+            }
+          }
+          ast::by_ref | ast::by_val | ast::by_move | ast::by_copy {}
+        }
+        alt arg.node {
+          ast::expr_fn_block(*) { blocks += [arg]; }
+          ast::expr_loop_body(b) { blocks += [b]; }
+          _ {
+            let root_var = path_def_id(*cx, root.ex);
+            let arg_copied = alt ty::resolved_mode(cx.tcx, arg_t.mode) {
+              ast::by_move | ast::by_copy { copied }
+              ast::by_mutbl_ref { not_allowed }
+              ast::by_ref | ast::by_val { not_copied }
+            };
+            visit_expr(cx, arg, sc, v);
+            bindings += [@{node_id: arg.id,
+                           span: arg.span,
+                           root_var: root_var,
+                           local_id: loc_id,
+                           unsafe_tys: unsafe_set(root.mutbl),
+                           mut copied: arg_copied}];
+          }
+        }
+    }
+    let f_may_close = alt f.node {
+      ast::expr_path(_) { def_is_local_or_self(cx.tcx.def_map.get(f.id)) }
+      _ { true }
+    };
+    if f_may_close {
+        let mut i = 0u;
+        for bindings.each {|b|
+            let mut unsfe = vec::len(b.unsafe_tys) > 0u;
+            alt b.root_var {
+              some(rid) {
+                for sc.bs.each {|o|
+                    if o.node_id == rid && vec::len(o.unsafe_tys) > 0u {
+                        unsfe = true; break;
+                    }
+                }
+              }
+              _ {}
+            }
+            if unsfe && cant_copy(*cx, b) {
+                err(*cx, f.span, #fmt["function may alias with argument \
+                                      %u, which is not immutably rooted", i]);
+            }
+            i += 1u;
+        }
+    }
+    let mut j = 0u;
+    for bindings.each {|b|
+        for b.unsafe_tys.each {|unsafe_ty|
+            vec::iteri(arg_ts) {|i, arg_t|
+                let mut_alias =
+                    (ast::by_mutbl_ref == ty::arg_mode(cx.tcx, arg_t));
+                alt args[i].node {
+                  ast::expr_fn_block(*) | ast::expr_loop_body(_) {}
+                  _ {
+                    if i != j && ty_can_unsafely_include(
+                        *cx, unsafe_ty, arg_t.ty, mut_alias) &&
+                       cant_copy(*cx, b) {
+                        err(*cx, args[i].span,
+                            #fmt["argument %u may alias with argument %u, \
+                                  which is not immutably rooted", i, j]);
+                    }
+                  }
+                }
+            }
+        }
+        j += 1u;
+    }
+
+    // Ensure we're not passing a root by mut alias.
+    for mut_roots.each {|mroot|
+        for bindings.each {|b|
+            if b.node_id != mroot.arg.id {
+                alt b.root_var {
+                  some(root) {
+                    if mroot.node == root && cant_copy(*cx, b) {
+                        err(*cx, mroot.arg.span,
+                            "passing a mut reference to a \
+                             variable that roots another reference");
+                        break;
+                    }
+                  }
+                  none { }
+                }
+            }
+        }
+    }
+    // Check the bodies of block arguments against the current scope
+    if blocks.len() > 0u {
+        let inner_sc = {bs: bindings + sc.bs, invalid: sc.invalid};
+        for blocks.each {|blk|
+            alt check blk.node {
+              ast::expr_fn_block(_, body, _) {
+                v.visit_block(body, inner_sc, v);
+              }
+            }
+        }
+        for bindings.each {|binding|
+            test_scope(*cx, sc, binding, none);
+        }
+    }
+}
+
+#[warn(no_non_implicitly_copyable_typarams)]
+fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
+             v: vt<scope>) {
+    v.visit_expr(input, sc, v);
+    let orig_invalid = *sc.invalid;
+    let mut all_invalid = orig_invalid;
+    let root = expr_root(cx, input, true);
+    for arms.each {|a|
+        let mut new_bs = sc.bs;
+        let root_var = path_def_id(cx, root.ex);
+        let pat_id_map = pat_util::pat_id_map(cx.tcx.def_map, a.pats[0]);
+        type info = {
+            id: node_id,
+            mut unsafe_tys: [unsafe_ty],
+            span: span};
+        let mut binding_info: [info] = [];
+        for a.pats.each {|pat|
+            for pattern_roots(cx.tcx, root.mutbl, pat).each {|proot|
+                let canon_id = pat_id_map.get(proot.name);
+                alt vec::find(binding_info, {|x| x.id == canon_id}) {
+                  some(s) { s.unsafe_tys += unsafe_set(proot.mutbl); }
+                  none {
+                      binding_info += [
+                          {id: canon_id,
+                           mut unsafe_tys: unsafe_set(proot.mutbl),
+                           span: proot.span}];
+                  }
+                }
+            }
+        }
+        for binding_info.each {|info|
+            new_bs += [mk_binding(cx, info.id, info.span, root_var,
+                                  copy info.unsafe_tys)];
+        };
+        *sc.invalid = orig_invalid;
+        visit::visit_arm(a, {bs: new_bs with sc}, v);
+        all_invalid = join_invalid(all_invalid, *sc.invalid);
+    };
+    *sc.invalid = all_invalid;
+}
+
+fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk,
+             sc: scope, v: vt<scope>) {
+    let root = expr_root(cx, seq, false);
+
+    // If this is a mut vector, don't allow it to be touched.
+    let seq_t = ty::expr_ty(cx.tcx, seq);
+    let mut cur_mutbl = root.mutbl;
+    alt ty::get(seq_t).struct {
+      ty::ty_vec(mt) {
+        if mt.mutbl != ast::m_imm {
+            cur_mutbl = some(contains(seq_t));
+        }
+      }
+      _ {}
+    }
+    let root_var = path_def_id(cx, root.ex);
+    let mut new_bs = sc.bs;
+    for pattern_roots(cx.tcx, cur_mutbl, local.node.pat).each {|proot|
+        new_bs += [mk_binding(cx, proot.id, proot.span, root_var,
+                              unsafe_set(proot.mutbl))];
+    }
+    visit::visit_block(blk, {bs: new_bs with sc}, v);
+}
+
+fn check_var(cx: ctx, ex: @ast::expr, p: @ast::path, id: ast::node_id,
+             assign: bool, sc: scope) {
+    let def = cx.tcx.def_map.get(id);
+    if !def_is_local_or_self(def) { ret; }
+    let my_defnum = ast_util::def_id_of_def(def).node;
+    let my_local_id = local_id_of_node(cx, my_defnum);
+    let var_t = ty::expr_ty(cx.tcx, ex);
+    for sc.bs.each {|b|
+        // excludes variables introduced since the alias was made
+        if my_local_id < b.local_id {
+            for b.unsafe_tys.each {|unsafe_ty|
+                if ty_can_unsafely_include(cx, unsafe_ty, var_t, assign) {
+                    let inv = @{reason: val_taken, node_id: b.node_id,
+                                sp: ex.span, path: p};
+                    *sc.invalid = @list::cons(inv, *sc.invalid);
+                }
+            }
+        } else if b.node_id == my_defnum {
+            test_scope(cx, sc, b, some(p));
+        }
+    }
+}
+
+fn check_lval(cx: @ctx, dest: @ast::expr, sc: scope, v: vt<scope>) {
+    alt dest.node {
+      ast::expr_path(p) {
+        let def = cx.tcx.def_map.get(dest.id);
+        let dnum = ast_util::def_id_of_def(def).node;
+        for sc.bs.each {|b|
+            if b.root_var == some(dnum) {
+                let inv = @{reason: overwritten, node_id: b.node_id,
+                            sp: dest.span, path: p};
+                *sc.invalid = @list::cons(inv, *sc.invalid);
+            }
+        }
+      }
+      _ { visit_expr(cx, dest, sc, v); }
+    }
+}
+
+fn check_if(c: @ast::expr, then: ast::blk, els: option<@ast::expr>,
+            sc: scope, v: vt<scope>) {
+    v.visit_expr(c, sc, v);
+    let orig_invalid = *sc.invalid;
+    v.visit_block(then, sc, v);
+    let then_invalid = *sc.invalid;
+    *sc.invalid = orig_invalid;
+    visit::visit_expr_opt(els, sc, v);
+    *sc.invalid = join_invalid(*sc.invalid, then_invalid);
+}
+
+fn check_loop(cx: ctx, sc: scope, checker: fn()) {
+    let orig_invalid = filter_invalid(*sc.invalid, sc.bs);
+    checker();
+    let new_invalid = filter_invalid(*sc.invalid, sc.bs);
+    // Have to check contents of loop again if it invalidated an alias
+    if list::len(orig_invalid) < list::len(new_invalid) {
+        let old_silent = cx.silent;
+        cx.silent = true;
+        checker();
+        cx.silent = old_silent;
+    }
+    *sc.invalid = new_invalid;
+}
+
+fn test_scope(cx: ctx, sc: scope, b: binding, p: option<@ast::path>) {
+    let mut prob = find_invalid(b.node_id, *sc.invalid);
+    alt b.root_var {
+      some(dn) {
+        for sc.bs.each {|other|
+            if !is_none(prob) { break; }
+            if other.node_id == dn {
+                prob = find_invalid(other.node_id, *sc.invalid);
+            }
+        }
+      }
+      _ {}
+    }
+    if !is_none(prob) && cant_copy(cx, b) {
+        let i = option::get(prob);
+        let msg = alt i.reason {
+          overwritten { "overwriting " + ast_util::path_name(i.path) }
+          val_taken { "taking the value of " + ast_util::path_name(i.path) }
+        };
+        let refname = alt p {
+          some(pt) { "reference " + ast_util::path_name(pt) +
+                        ", which is still used" }
+          none { "an argument" }
+        };
+        err(cx, i.sp, msg + " will invalidate " + refname);
+    }
+}
+
+fn path_def(cx: ctx, ex: @ast::expr) -> option<ast::def> {
+    ret alt ex.node {
+          ast::expr_path(_) { some(cx.tcx.def_map.get(ex.id)) }
+          _ { none }
+        }
+}
+
+fn path_def_id(cx: ctx, ex: @ast::expr) -> option<ast::node_id> {
+    alt ex.node {
+      ast::expr_path(_) {
+        ret some(ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)).node);
+      }
+      _ { ret none; }
+    }
+}
+
+fn ty_can_unsafely_include(cx: ctx, needle: unsafe_ty, haystack: ty::t,
+                           mutbl: bool) -> bool {
+    fn get_mutbl(cur: bool, mt: ty::mt) -> bool {
+        ret cur || mt.mutbl != ast::m_imm;
+    }
+    fn helper(tcx: ty::ctxt, needle: unsafe_ty, haystack: ty::t, mutbl: bool)
+        -> bool {
+        if alt needle {
+          contains(ty) { ty == haystack }
+          mutbl_contains(ty) { mutbl && ty == haystack }
+        } { ret true; }
+        alt ty::get(haystack).struct {
+          ty::ty_enum(_, ts) {
+            for ts.tps.each {|t|
+                if helper(tcx, needle, t, mutbl) { ret true; }
+            }
+            ret false;
+          }
+          ty::ty_box(mt) | ty::ty_ptr(mt) | ty::ty_uniq(mt) {
+            ret helper(tcx, needle, mt.ty, get_mutbl(mutbl, mt));
+          }
+          ty::ty_rec(fields) {
+            for fields.each {|f|
+                if helper(tcx, needle, f.mt.ty, get_mutbl(mutbl, f.mt)) {
+                    ret true;
+                }
+            }
+            ret false;
+          }
+          ty::ty_tup(ts) {
+            for ts.each {|t| if helper(tcx, needle, t, mutbl) { ret true; } }
+            ret false;
+          }
+          ty::ty_fn({proto: ast::proto_bare, _}) { ret false; }
+          // These may contain anything.
+          ty::ty_fn(_) | ty::ty_iface(_, _) { ret true; }
+          // A type param may include everything, but can only be
+          // treated as opaque downstream, and is thus safe unless we
+          // saw mut fields, in which case the whole thing can be
+          // overwritten.
+          ty::ty_param(_, _) { ret mutbl; }
+          _ { ret false; }
+        }
+    }
+    ret helper(cx.tcx, needle, haystack, mutbl);
+}
+
+fn def_is_local_or_self(d: ast::def) -> bool {
+    alt d {
+      ast::def_local(_, _) | ast::def_arg(_, _) | ast::def_binding(_) |
+      ast::def_upvar(_, _, _) | ast::def_self(_) { true }
+      _ { false }
+    }
+}
+
+fn local_id_of_node(cx: ctx, id: node_id) -> uint {
+    alt cx.tcx.items.find(id) {
+      some(ast_map::node_arg(_, id)) | some(ast_map::node_local(id)) { id }
+      _ { 0u }
+    }
+}
+
+// Heuristic, somewhat random way to decide whether to warn when inserting an
+// implicit copy.
+fn copy_is_expensive(tcx: ty::ctxt, ty: ty::t) -> bool {
+    fn score_ty(tcx: ty::ctxt, ty: ty::t) -> uint {
+        ret alt ty::get(ty).struct {
+          ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_int(_) |
+          ty::ty_uint(_) | ty::ty_float(_) | ty::ty_type |
+          ty::ty_ptr(_) { 1u }
+          ty::ty_box(_) | ty::ty_iface(_, _) { 3u }
+          ty::ty_constr(t, _) | ty::ty_res(_, t, _) { score_ty(tcx, t) }
+          ty::ty_fn(_) { 4u }
+          ty::ty_str | ty::ty_vec(_) | ty::ty_param(_, _) { 50u }
+          ty::ty_uniq(mt) { 1u + score_ty(tcx, mt.ty) }
+          ty::ty_enum(_, substs) {
+            substs.tps.foldl(0u) { |sum, t| sum + score_ty(tcx, t) }
+          }
+          ty::ty_tup(ts) {
+            ts.foldl(0u) { |sum, t| sum + score_ty(tcx, t) }
+          }
+          ty::ty_rec(fs) {
+            let mut sum = 0u;
+            for fs.each {|f| sum += score_ty(tcx, f.mt.ty); }
+            sum
+          }
+          _ {
+            tcx.sess.warn(#fmt("score_ty: unexpected type %s",
+               ty_to_str(tcx, ty)));
+            1u // ???
+          }
+        };
+    }
+    ret score_ty(tcx, ty) > 8u;
+}
+
+type pattern_root = {id: node_id,
+                     name: ident,
+                     mutbl: option<unsafe_ty>,
+                     span: span};
+
+fn pattern_roots(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat)
+    -> [pattern_root] {
+    fn walk(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat,
+            &set: [pattern_root]) {
+        alt pat.node {
+          ast::pat_ident(nm, sub)
+          if !pat_util::pat_is_variant(tcx.def_map, pat) {
+            set += [{id: pat.id, name: path_to_ident(nm), mutbl: mutbl,
+                        span: pat.span}];
+            option::iter(sub) {|p| walk(tcx, mutbl, p, set); };
+          }
+          ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) |
+          ast::pat_ident(_, _) | ast::pat_enum(_, none) {}
+          ast::pat_enum(_, some(ps)) | ast::pat_tup(ps) {
+            for ps.each {|p| walk(tcx, mutbl, p, set); }
+          }
+          ast::pat_rec(fs, _) {
+            let ty = ty::node_id_to_type(tcx, pat.id);
+            for fs.each {|f|
+                let m = ty::get_field(ty, f.ident).mt.mutbl != ast::m_imm,
+                    c = if m { some(contains(ty)) } else { mutbl };
+                walk(tcx, c, f.pat, set);
+            }
+          }
+          ast::pat_box(p) {
+            let ty = ty::node_id_to_type(tcx, pat.id);
+            let m = alt ty::get(ty).struct {
+              ty::ty_box(mt) { mt.mutbl != ast::m_imm }
+              _ { tcx.sess.span_bug(pat.span, "box pat has non-box type"); }
+            },
+                c = if m  {some(contains(ty)) } else { mutbl };
+            walk(tcx, c, p, set);
+          }
+          ast::pat_uniq(p) {
+            let ty = ty::node_id_to_type(tcx, pat.id);
+            let m = alt ty::get(ty).struct {
+              ty::ty_uniq(mt) { mt.mutbl != ast::m_imm }
+              _ { tcx.sess.span_bug(pat.span, "uniq pat has non-uniq type"); }
+            },
+                c = if m { some(contains(ty)) } else { mutbl };
+            walk(tcx, c, p, set);
+          }
+        }
+    }
+    let mut set = [];
+    walk(tcx, mutbl, pat, set);
+    ret set;
+}
+
+enum deref_t { unbox(bool), field, index, }
+
+type deref = @{mutbl: bool, kind: deref_t, outer_t: ty::t};
+
+fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool)
+    -> {ex: @ast::expr, mutbl: option<unsafe_ty>} {
+
+    fn maybe_auto_unbox(tcx: ty::ctxt, t: ty::t) -> {t: ty::t, ds: [deref]} {
+        let mut ds = [], t = t;
+        loop {
+            alt ty::get(t).struct {
+              ty::ty_box(mt) | ty::ty_uniq(mt) | ty::ty_rptr(_, mt) {
+                ds += [@{mutbl: mt.mutbl == ast::m_mutbl,
+                         kind: unbox(false),
+                         outer_t: t}];
+                t = mt.ty;
+              }
+              ty::ty_res(_, inner, substs) {
+                ds += [@{mutbl: false, kind: unbox(false), outer_t: t}];
+                t = ty::subst(tcx, substs, inner);
+              }
+              ty::ty_enum(did, substs) {
+                let variants = ty::enum_variants(tcx, did);
+                if vec::len(*variants) != 1u ||
+                    vec::len(variants[0].args) != 1u {
+                    break;
+                }
+                ds += [@{mutbl: false, kind: unbox(false), outer_t: t}];
+                t = ty::subst(tcx, substs, variants[0].args[0]);
+              }
+              _ { break; }
+            }
+        }
+        ret {t: t, ds: ds};
+    }
+
+    fn expr_root_(tcx: ty::ctxt, ctor_self: option<node_id>,
+                  ex: @ast::expr, autoderef: bool) -> {ex: @ast::expr,
+                                                       ds: @[deref]} {
+        let mut ds: [deref] = [], ex = ex;
+        loop {
+            alt copy ex.node {
+              ast::expr_field(base, ident, _) {
+                let auto_unbox =
+                    maybe_auto_unbox(tcx, ty::expr_ty(tcx, base));
+                let mut is_mutbl = false;
+                alt ty::get(auto_unbox.t).struct {
+                  ty::ty_rec(fields) {
+                    for fields.each {|fld|
+                        if str::eq(ident, fld.ident) {
+                            is_mutbl = fld.mt.mutbl == ast::m_mutbl;
+                            break;
+                        }
+                    }
+                  }
+                  ty::ty_class(did, _) {
+                    util::common::log_expr(*base);
+                    let in_self = alt ctor_self {
+                      some(selfid) {
+                        alt tcx.def_map.find(base.id) {
+                          some(ast::def_self(slfid)) { slfid == selfid }
+                          _ { false }
+                        }
+                      }
+                      none { false }
+                    };
+                    for ty::lookup_class_fields(tcx, did).each {|fld|
+                        if str::eq(ident, fld.ident) {
+                            is_mutbl = fld.mutability == ast::class_mutable
+                                || in_self; // all fields can be mutated
+                            // in the ctor
+                            break;
+                        }
+                    }
+                  }
+                  _ {}
+                }
+                ds += [@{mutbl:is_mutbl, kind:field, outer_t:auto_unbox.t}];
+                ds += auto_unbox.ds;
+                ex = base;
+              }
+              ast::expr_index(base, _) {
+                let auto_unbox =
+                    maybe_auto_unbox(tcx, ty::expr_ty(tcx, base));
+                alt ty::get(auto_unbox.t).struct {
+                  ty::ty_evec(mt, _) |
+                  ty::ty_vec(mt) {
+                    ds +=
+                        [@{mutbl: mt.mutbl == ast::m_mutbl,
+                           kind: index,
+                           outer_t: auto_unbox.t}];
+                  }
+                  ty::ty_estr(_) |
+                  ty::ty_str {
+                    ds += [@{mutbl:false, kind:index, outer_t:auto_unbox.t}];
+                  }
+                  _ { break; }
+                }
+                ds += auto_unbox.ds;
+                ex = base;
+              }
+              ast::expr_unary(op, base) {
+                if op == ast::deref {
+                    let base_t = ty::expr_ty(tcx, base);
+                    let mut is_mutbl = false, ptr = false;
+                    alt ty::get(base_t).struct {
+                      ty::ty_box(mt) { is_mutbl = mt.mutbl==ast::m_mutbl; }
+                      ty::ty_uniq(mt) { is_mutbl = mt.mutbl==ast::m_mutbl; }
+                      ty::ty_res(_, _, _) { }
+                      ty::ty_enum(_, _) { }
+                      ty::ty_ptr(mt) | ty::ty_rptr(_, mt) {
+                        is_mutbl = mt.mutbl==ast::m_mutbl;
+                        ptr = true;
+                      }
+                      _ {
+                        tcx.sess.span_bug(
+                            base.span,
+                            "ill-typed base expression in deref"); }
+                    }
+                    ds += [@{mutbl: is_mutbl, kind: unbox(ptr && is_mutbl),
+                             outer_t: base_t}];
+                    ex = base;
+                } else { break; }
+              }
+              _ { break; }
+            }
+        }
+        if autoderef {
+            let auto_unbox = maybe_auto_unbox(tcx, ty::expr_ty(tcx, ex));
+            ds += auto_unbox.ds;
+        }
+        ret {ex: ex, ds: @ds};
+    }
+
+    let base_root = expr_root_(cx.tcx, none, ex, autoderef);
+    let mut unsafe_ty = none;
+    for vec::each(*base_root.ds) {|d|
+        if d.mutbl { unsafe_ty = some(contains(d.outer_t)); break; }
+    }
+    ret {ex: base_root.ex, mutbl: unsafe_ty};
+}
+
+fn unsafe_set(from: option<unsafe_ty>) -> [unsafe_ty] {
+    alt from { some(t) { [t] } _ { [] } }
+}
+
+fn find_invalid(id: node_id, lst: @list<@invalid>) -> option<@invalid> {
+    let mut cur = lst;
+    loop {
+        alt *cur {
+          list::nil { ret none; }
+          list::cons(head, tail) {
+            if head.node_id == id { ret some(head); }
+            cur = tail;
+          }
+        }
+    };
+}
+
+fn join_invalid(a: @list<@invalid>, b: @list<@invalid>) -> @list<@invalid> {
+    let mut result = a;
+    list::iter(b) {|elt|
+        let mut found = false;
+        list::iter(a) {|e| if e == elt { found = true; } }
+        if !found { result = @list::cons(elt, result); }
+    }
+    result
+}
+
+fn filter_invalid(src: @list<@invalid>, bs: [binding]) -> @list<@invalid> {
+    let mut out = @list::nil, cur = src;
+    loop {
+        alt *cur {
+          list::cons(head, tail) {
+            let p = vec::position(bs, {|b| b.node_id == head.node_id});
+            if !is_none(p) { out = @list::cons(head, out); }
+            cur = tail;
+          }
+          list::nil {
+            ret out;
+          }
+        }
+    }
+}
+
+fn err(cx: ctx, sp: span, err: str) {
+    if !cx.silent || !cx.tcx.sess.has_errors() {
+        cx.tcx.sess.span_err(sp, err);
+    }
+}
+
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
index d604505455cb517bd752a6bcd085f34faad36a2e..bcf6c799739f78c4aeab4cd4560740b5f1bba3eb 100644 (file)
@@ -52,6 +52,7 @@
 type maps = {
     mutbl_map: middle::borrowck::mutbl_map,
     root_map: middle::borrowck::root_map,
+    copy_map: middle::alias::copy_map,
     last_use_map: middle::liveness::last_use_map,
     impl_map: middle::resolve::impl_map,
     method_map: middle::typeck::method_map,
@@ -830,6 +831,12 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
         }
     }
 
+    option::iter(maps.copy_map.find(id)) {|_m|
+        ebml_w.tag(c::tag_table_copy) {||
+            ebml_w.id(id);
+        }
+    }
+
     option::iter(maps.last_use_map.find(id)) {|m|
         ebml_w.tag(c::tag_table_last_use) {||
             ebml_w.id(id);
@@ -936,6 +943,8 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
 
         if tag == (c::tag_table_mutbl as uint) {
             dcx.maps.mutbl_map.insert(id, ());
+        } else if tag == (c::tag_table_copy as uint) {
+            dcx.maps.copy_map.insert(id, ());
         } else {
             let val_doc = entry_doc[c::tag_table_val];
             let val_dsr = ebml::ebml_deserializer(val_doc);
index 067140500102ea858f4404423dcf0066254792a6..d106c2b23d86edf36767ecf40923f5a68a5c4068 100644 (file)
 fn check_crate(tcx: ty::ctxt,
                method_map: typeck::method_map,
                crate: @ast::crate) -> (root_map, mutbl_map) {
+
+    // big hack to keep this off except when I want it on
+    let msg_level = if tcx.sess.opts.borrowck != 0u {
+        tcx.sess.opts.borrowck
+    } else {
+        os::getenv("RUST_BORROWCK").map_default(0u) { |v|
+            option::get(uint::from_str(v))
+        }
+    };
+
     let bccx = @{tcx: tcx,
                  method_map: method_map,
+                 msg_level: msg_level,
                  root_map: root_map(),
                  mutbl_map: int_hash()};
 
-    let req_maps = gather_loans::gather_loans(bccx, crate);
+    let req_maps = if msg_level > 0u {
+        gather_loans::gather_loans(bccx, crate)
+    } else {
+        {req_loan_map: int_hash(),
+         pure_map: int_hash()}
+    };
     check_loans::check_loans(bccx, req_maps, crate);
     ret (bccx.root_map, bccx.mutbl_map);
 }
@@ -186,6 +202,7 @@ fn check_crate(tcx: ty::ctxt,
 
 type borrowck_ctxt = @{tcx: ty::ctxt,
                        method_map: typeck::method_map,
+                       msg_level: uint,
                        root_map: root_map,
                        mutbl_map: mutbl_map};
 
@@ -346,7 +363,11 @@ fn report(err: bckerr) {
     }
 
     fn span_err(s: span, m: str) {
-        self.tcx.sess.span_err(s, m);
+        if self.msg_level == 1u {
+            self.tcx.sess.span_warn(s, m);
+        } else {
+            self.tcx.sess.span_err(s, m);
+        }
     }
 
     fn span_note(s: span, m: str) {
index 44f26ad69470f784e5cf72605b4f1ae03c229027..f31f3a209bcd1b54745dc7500041dab50b3cf33a 100644 (file)
@@ -614,7 +614,25 @@ fn make_phi_bindings(bcx: block, map: [exit_node],
             bcx.fcx.lllocals.insert(node_id, local_mem(local));
         } else { success = false; }
     };
-    if !success {
+    if success {
+        // Copy references that the alias analysis considered unsafe
+        for ids.each_value {|node_id|
+            if bcx.ccx().maps.copy_map.contains_key(node_id) {
+                let local = alt bcx.fcx.lllocals.find(node_id) {
+                  some(local_mem(x)) { x }
+                  _ { bcx.tcx().sess.bug("someone \
+                        forgot to document an invariant in \
+                        make_phi_bindings"); }
+                };
+                let e_ty = node_id_type(bcx, node_id);
+                let alloc = alloc_ty(bcx, e_ty);
+                bcx = copy_val(bcx, INIT, alloc,
+                               load_if_immediate(bcx, local, e_ty), e_ty);
+                add_clean(bcx, alloc, e_ty);
+                bcx.fcx.lllocals.insert(node_id, local_mem(alloc));
+            }
+        };
+    } else {
         Unreachable(bcx);
     }
     ret success;
@@ -701,7 +719,7 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
     alt pat.node {
       ast::pat_ident(_,inner) {
         if pat_is_variant(bcx.tcx().def_map, pat) { ret bcx; }
-        if make_copy {
+        if make_copy || ccx.maps.copy_map.contains_key(pat.id) {
             let ty = node_id_type(bcx, pat.id);
             let llty = type_of::type_of(ccx, ty);
             let alloc = alloca(bcx, llty);
index 93f659dfccffd2a6d6a3294ab5b7c04044bb767e..34845ceb45ab58c6379e980e0b1ddec051c5b906 100644 (file)
@@ -2888,11 +2888,22 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
         // to have type lldestty (the callee's expected type).
         val = llvm::LLVMGetUndef(lldestty);
     } else if arg_mode == ast::by_ref || arg_mode == ast::by_val {
+        let mut copied = false;
         let imm = ty::type_is_immediate(arg.ty);
         #debug["   arg.ty=%s, imm=%b, arg_mode=%?, lv.kind=%?",
                ty_to_str(bcx.tcx(), arg.ty), imm, arg_mode, lv.kind];
         if arg_mode == ast::by_ref && lv.kind != owned && imm {
             val = do_spill_noroot(bcx, val);
+            copied = true;
+        }
+        if ccx.maps.copy_map.contains_key(e.id) && lv.kind != temporary {
+            if !copied {
+                let alloc = alloc_ty(bcx, arg.ty);
+                bcx = copy_val(bcx, INIT, alloc,
+                               load_if_immediate(bcx, val, arg.ty), arg.ty);
+                val = alloc;
+            } else { bcx = take_ty(bcx, val, arg.ty); }
+            add_clean(bcx, val, arg.ty);
         }
         if arg_mode == ast::by_val && (lv.kind == owned || !imm) {
             val = Load(bcx, val);
index c0007b0a13f0ace0501120428d3b65b7f9803d0f..cf929353ab4ea45c344ba2f55a6ace30a68199cb 100644 (file)
@@ -19,13 +19,11 @@ enum reflector = {
 impl methods for reflector {
 
     fn c_uint(u: uint) -> ValueRef {
-        let bcx = self.bcx;
-        C_uint(bcx.ccx(), u)
+        C_uint(self.bcx.ccx(), u)
     }
 
     fn visit(ty_name: str, args: [ValueRef]) {
-        let bcx = self.bcx;
-        let tcx = bcx.tcx();
+        let tcx = self.bcx.tcx();
         let mth_idx = option::get(ty::method_idx("visit_" + ty_name,
                                                  *self.visitor_methods));
         let mth_ty = ty::mk_fn(tcx, self.visitor_methods[mth_idx].fty);
index 7d112ae71467c3cc7895e5954ce184c70e72c6b6..7cdaeb9273be061135cd8bc4ce7954950600f5df 100644 (file)
@@ -78,6 +78,7 @@ mod middle {
         mod loan;
         mod preserve;
     }
+    mod alias;
     mod liveness;
     mod block_use;
     mod kind;
index a83f727369e187726f77e8d5066ae8321449001a..c418c855237357276de164065af552e78aace65d 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 type point = { x: int, y: int };
 
 fn a() {
index 2e045b84c603a86d8d758ac39ae675f197ee7312..c8830957c763533e55c8c838bab0c9484fa350c9 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 type point = { x: int, y: int };
 
 fn a() {
index ef8914b134fea741bad974e2dd30c0d0ab8bfd13..aa2358121e28abcc72a827c724d7280fa4a131b8 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn borrow(_v: &int) {}
 
 fn borrow_from_arg_imm_ref(&&v: ~int) {
index e391686741248f5a98d6e890aa9376728710db2e..def99033e5ef902fcb72e2df920690ea6dd3c467 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 // Note: the borrowck analysis is currently flow-insensitive.
 // Therefore, some of these errors are marked as spurious and could be
 // corrected by a simple change to the analysis.  The others are
index 0862af2e6fab7b99b25ad0c66f01afe4e4caeb38..d9f3bd0567680beeb88f603d9011dfd1ac623ea6 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn borrow(v: &int, f: fn(x: &int)) {
     f(v);
 }
index b5044754ac0b23a108dc5c5bb3d47058cdf6e37d..6c26aafd425e3bce0313bfaf2778debaafc35886 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn take(-_v: ~int) {
 }
 
index 186229f39bd618f4a8674a0fcaf291bbca36d2bf..d7538f168abd268bb8fab15495df3289d587641a 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn borrow(v: &int, f: fn(x: &int)) {
     f(v);
 }
index a268b37ca72eb69741bba760fe040c532dc8b0c5..7f45a50cbf40356a98de66c44461e3bf8cf1b8f5 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 type point = { x: int, y: int };
 
 impl foo for point {
index ec543bfc4f97ef2159173da1fd619a5344d2510e..1fba825d5aba207610ae63e2f89de9e9c4ea55fc 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 type point = { x: int, y: int };
 
 impl foo for point {
index 80e23570a0dd3081ef6f9f269cf3984b86532b86..c3d0a62f05e4d64d3d1dfd9e5660711ddea4c2a5 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 // Here we check that it is allowed to lend out an element of a
 // (locally rooted) mutable, unique vector, and that we then prevent
 // modifications to the contents.
index c14c35189b50ac28a6db395b56d3c24f5fc2ad72..803f30f8e33a7e4cd48d59a5dfbeb1c4e99ce125 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn want_slice(v: [int]/&) -> int {
     let mut sum = 0;
     for vec::each(v) { |i| sum += i; }
diff --git a/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs b/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs
deleted file mode 100644 (file)
index a546ef8..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-enum cycle {
-    node({mut a: ~cycle}),
-    empty
-}
-fn main() {
-    let x = ~node({mut a: ~empty});
-    // Create a cycle!
-    alt check *x { //! NOTE loan of immutable local variable granted here
-      node(y) {
-        y.a <- x; //! ERROR moving out of immutable local variable prohibited due to outstanding loan
-      }
-    };
-}
\ No newline at end of file
index 29de06a11041b19fdcc027b0cc9e20222650a0d3..32135b5ad8f68d41b049165efeb4a56329aea485 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn match_imm_box(v: &const @option<int>) -> int {
     alt *v {
       @some(i) {i}
index 8e87e5d7420eb58d1e11a306867ed28c86893757..753b51b0251c75d827fb13e1af74363ea3fbf3ce 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn match_ref(&&v: option<int>) -> int {
     alt v {
       some(i) {
index cf1199557bda8241fdc4adb37146cc85fd829299..5018d4835ae4c132a94f777322061953802e9b08 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // xfail-pretty -- comments are infaithfully preserved
 
 fn main() {
index db8129d005ace9bba19b3dfebfdeb673aa047543..bf28af2db4d0bfd99327df253445d6db84e7859f 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // xfail-pretty -- comments are infaithfully preserved
 
 fn main() {
index 4469dca362065a351265886ab2683fa0d329bb2e..9e83fd4be355d7f65e870f622f4d77a99b2fd79e 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 pure fn pure_borrow(_x: &int, _y: ()) {}
 
 fn test1(x: @mut ~int) {
index a5f8b947bf2e5bf0d789a62a52614e3e8182b993..cefe3ba05538bcf3b0aa9ff28956eeb51ba23b58 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn impure(_i: int) {}
 
 // check that unchecked alone does not override borrowck:
index cf0f5439afc9bea90728c93bf016354cbb6cd894..4433dba0c19a369b83687212cbc907acdd6cd58c 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn borrow(_v: &int) {}
 
 fn box_mut(v: @mut ~int) {
index 78bba1576e8d1be74144e9ad61b5a40cb6998eeb..787285e7bf5ac262d13314c25f7542b6feadff2a 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn borrow(_v: &int) {}
 
 fn local() {
index 156106b880c4f43d55f267b54206a47db32007e4..b0fae6622d002532dcbfac3bebb4b04a29d59336 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 fn borrow(_v: &int) {}
 
 fn box_mut(v: &mut ~int) {
index 793bb48c79a7d20493c61daac933a07cce12d64d..40d25a375668094e1bc95d18b6cad4054c716943 100644 (file)
@@ -8,6 +8,8 @@ fn f<T>(&o: option<T>) {
 fn main() {
     f::<int>(option::none);
     //!^ ERROR taking mut reference to static item
-    //!^^ ERROR illegal borrow unless pure: creating mutable alias to aliasable, immutable memory
-    //!^^^ NOTE impure due to access to impure function
+
+    // Additional errors reported by borrowck:
+    //^^ ERROR illegal borrow unless pure: creating mutable alias to aliasable, immutable memory
+    //^^^ NOTE impure due to access to impure function
 }
\ No newline at end of file
diff --git a/src/test/compile-fail/unsafe-alias-2.rs b/src/test/compile-fail/unsafe-alias-2.rs
new file mode 100644 (file)
index 0000000..ac20aee
--- /dev/null
@@ -0,0 +1,8 @@
+// error-pattern:invalidate reference x
+
+fn whoknows(x: @mut {mut x: int}) { x.x = 10; }
+
+fn main() {
+    let box = @mut {mut x: 1};
+    alt *box { x { whoknows(box); log(error, x); } }
+}
diff --git a/src/test/compile-fail/unsafe-alias.rs b/src/test/compile-fail/unsafe-alias.rs
new file mode 100644 (file)
index 0000000..0b13c5b
--- /dev/null
@@ -0,0 +1,10 @@
+// error-pattern:may alias with argument
+
+fn foo(x: {mut x: int}, f: fn@()) { log(debug, x); }
+
+fn whoknows(x: @mut {mut x: int}) { *x = {mut x: 10}; }
+
+fn main() {
+    let box = @mut {mut x: 1};
+    foo(*box, bind whoknows(box));
+}
diff --git a/src/test/compile-fail/unsafe-alt.rs b/src/test/compile-fail/unsafe-alt.rs
new file mode 100644 (file)
index 0000000..498508d
--- /dev/null
@@ -0,0 +1,8 @@
+// error-pattern:invalidate reference i
+
+enum foo { left({mut x: int}), right(bool) }
+
+fn main() {
+    let mut x = left({mut x: 10});
+    alt x { left(i) { x = right(false); copy x; log(debug, i); } _ { } }
+}
diff --git a/src/test/compile-fail/unsafe-mutable-alias.rs b/src/test/compile-fail/unsafe-mutable-alias.rs
new file mode 100644 (file)
index 0000000..1767eb3
--- /dev/null
@@ -0,0 +1,8 @@
+// error-pattern:mut reference to a variable that roots another reference
+
+fn f(a: {mut x: int}, &b: {mut x: int}) -> int {
+    b.x += 1;
+    ret a.x + b.x;
+}
+
+fn main() { let i = {mut x: 4}; log(debug, f(i, i)); }
index bef4af11be4734eb22230237f7713fedc66e9be2..bffa76efa302a121f49e5c26064f11c9c96a0c68 100644 (file)
@@ -1,6 +1,6 @@
 fn main() {
     let x = ~{mut a: ~10, b: ~20};
     alt x {
-      ~{a, b} { assert *a == 10; (*x).a = ~30; assert *a == 30; }
+      ~{a, b} { assert *a == 10; (*x).a = ~30; assert *a == 10; }
     }
 }
index 63cbdf6ca791b2f84df089baad68f9587a395153..43de34e87a83e98622e06fbf9cc46b03ad70466b 100644 (file)
@@ -1,6 +1,6 @@
 fn main() {
     let x = @{mut a: @10, b: @20};
     alt x {
-      @{a, b} { assert *a == 10; (*x).a = @30; assert *a == 30; }
+      @{a, b} { assert *a == 10; (*x).a = @30; assert *a == 10; }
     }
 }
index a6279d12ab8cf73ab9968c63b766692d12094f09..e6e4c36db35db07c66b12388e03a33ca99fde53c 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn want_slice(v: [int]/&) -> int {
     let mut sum = 0;
     for vec::each(v) { |i| sum += i; }
index 73a7085d86578ad4c685677bb52fac11da9f088c..e5dd8a1756c28ff8775d376cf47447ad87412bd6 100644 (file)
@@ -1,3 +1,6 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
+
 fn main() {
     let mut x = none;
     alt x {
index 9b49e3cd04459f5633f50bd4772d0c3538c775cb..5241848131de4f88ca8189a9a8c60c5606587c0a 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast   (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn main() {
index f486ffe557d091ab30fc7bcc730d54e91b997fc7..044db44595d13bcfe9eaf47d86b5d0185516d0c0 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn main() {
index 09353e3d39f258d5e3ca1cf138d2ee60aff64464..06260dbe5d2a9e7af32fc1cc4fb9d5331ce53414 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn borrow(x: &int, f: fn(x: &int)) {
index 0bad47642a041e59f62ef6398f4ee2b5abd7f3b7..718507d1efca78af13e190dd668b818eeddc38ae 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn main() {
index 475e5eaea8c55ba4f3e8e28ed654996e3ef9dbd4..3bec4fb083a07e5b36f59de1216fa6bc26231a54 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn borrow(x: &int, f: fn(x: &int)) {
index 285b3ad8e0f30fb9b29daa8cfe371837a25822c4..ed6e0b19e01db365a0d74b8c6c27a382f20804f3 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast   (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn switcher(x: option<@int>) {
index d4d2f5a205585318cf65dc493ac5e119f2b20435..ebc1c1ab70dc63def3317e7d310175ccbff6f1c8 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn borrow(x: &int, f: fn(x: &int)) {
index 0ee7ce187619232c970dab97ccac41d3f8da9665..d1098a04d01b566823f76c02e1352cc7cc6dfa73 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn testfn(cond: bool) {
index 0778b895137313483cda4490bc0b7038132fa354..c50afeeaa90a2319c52ff6f4f1af8c116e645b4b 100644 (file)
@@ -1,3 +1,5 @@
+// xfail-fast  (compile-flags unsupported on windows)
+// compile-flags:--borrowck=err
 // exec-env:RUST_POISON_ON_FREE=1
 
 fn borrow(x: &int, f: fn(x: &int)) {
index 38a7c7e160c8837a71df996b257bef18851da05c..7cd373756f7db37357be680389803ec48207b22c 100644 (file)
@@ -91,7 +91,7 @@ fn main() {
     intrinsic::visit_ty::<i16>(vv);
     intrinsic::visit_ty::<[int]>(vv);
 
-    for (copy v.types).each {|s|
+    for v.types.each {|s|
         io::println(#fmt("type: %s", s));
     }
     assert v.types == ["bool", "int", "i8", "i16",