about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2013-03-15 15:24:24 -0400
committerNiko Matsakis <niko@alum.mit.edu>2013-04-30 06:59:32 -0400
commita896440ca1b93cc5dc6edd827ea2ae89602bfa9e (patch)
treec6945d51bf84faeb9be6ac32247c8ffa2cd39226 /src
parentb5a7e8b35322869b1cf51bd1b35a86e9e721da54 (diff)
downloadrust-a896440ca1b93cc5dc6edd827ea2ae89602bfa9e.tar.gz
rust-a896440ca1b93cc5dc6edd827ea2ae89602bfa9e.zip
new borrow checker (mass squash)
Diffstat (limited to 'src')
-rw-r--r--src/libcore/cleanup.rs40
-rw-r--r--src/libcore/io.rs14
-rw-r--r--src/libcore/rt/sched/mod.rs2
-rw-r--r--src/libcore/str.rs12
-rw-r--r--src/libcore/to_bytes.rs2
-rw-r--r--src/libcore/unstable/lang.rs47
-rw-r--r--src/libcore/vec.rs11
-rw-r--r--src/librustc/driver/driver.rs21
-rw-r--r--src/librustc/driver/session.rs8
-rw-r--r--src/librustc/front/test.rs5
-rw-r--r--src/librustc/metadata/decoder.rs4
-rw-r--r--src/librustc/metadata/tydecode.rs3
-rw-r--r--src/librustc/metadata/tyencode.rs48
-rw-r--r--src/librustc/middle/astencode.rs43
-rw-r--r--src/librustc/middle/borrowck/check_loans.rs1063
-rw-r--r--src/librustc/middle/borrowck/doc.rs750
-rw-r--r--src/librustc/middle/borrowck/gather_loans.rs643
-rw-r--r--src/librustc/middle/borrowck/gather_loans/lifetime.rs322
-rw-r--r--src/librustc/middle/borrowck/gather_loans/mod.rs710
-rw-r--r--src/librustc/middle/borrowck/gather_loans/restrictions.rs251
-rw-r--r--src/librustc/middle/borrowck/loan.rs312
-rw-r--r--src/librustc/middle/borrowck/mod.rs861
-rw-r--r--src/librustc/middle/borrowck/preserve.rs409
-rw-r--r--src/librustc/middle/const_eval.rs2
-rw-r--r--src/librustc/middle/dataflow.rs1009
-rw-r--r--src/librustc/middle/kind.rs8
-rw-r--r--src/librustc/middle/lang_items.rs66
-rw-r--r--src/librustc/middle/liveness.rs105
-rw-r--r--src/librustc/middle/mem_categorization.rs546
-rw-r--r--src/librustc/middle/moves.rs28
-rw-r--r--src/librustc/middle/region.rs318
-rw-r--r--src/librustc/middle/resolve.rs12
-rw-r--r--src/librustc/middle/trans/_match.rs7
-rw-r--r--src/librustc/middle/trans/base.rs109
-rw-r--r--src/librustc/middle/trans/callee.rs2
-rw-r--r--src/librustc/middle/trans/common.rs9
-rw-r--r--src/librustc/middle/trans/consts.rs11
-rw-r--r--src/librustc/middle/trans/datum.rs7
-rw-r--r--src/librustc/middle/trans/expr.rs54
-rw-r--r--src/librustc/middle/trans/inline.rs39
-rw-r--r--src/librustc/middle/trans/meth.rs5
-rw-r--r--src/librustc/middle/trans/monomorphize.rs4
-rw-r--r--src/librustc/middle/trans/reachable.rs58
-rw-r--r--src/librustc/middle/ty.rs100
-rw-r--r--src/librustc/middle/typeck/check/_match.rs6
-rw-r--r--src/librustc/middle/typeck/check/method.rs47
-rw-r--r--src/librustc/middle/typeck/check/mod.rs54
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs571
-rw-r--r--src/librustc/middle/typeck/check/regionmanip.rs3
-rw-r--r--src/librustc/middle/typeck/check/writeback.rs29
-rw-r--r--src/librustc/middle/typeck/coherence.rs12
-rw-r--r--src/librustc/middle/typeck/infer/coercion.rs48
-rw-r--r--src/librustc/middle/typeck/infer/mod.rs81
-rw-r--r--src/librustc/middle/typeck/infer/region_inference.rs253
-rw-r--r--src/librustc/middle/typeck/infer/unify.rs35
-rw-r--r--src/librustc/util/ppaux.rs17
-rw-r--r--src/libstd/arc.rs12
-rw-r--r--src/libstd/bitv.rs14
-rw-r--r--src/libstd/net_tcp.rs6
-rw-r--r--src/libstd/serialize.rs15
-rw-r--r--src/libstd/sort.rs70
-rw-r--r--src/libstd/std.rc1
-rw-r--r--src/libsyntax/ast_map.rs69
-rw-r--r--src/libsyntax/ast_util.rs24
-rw-r--r--src/libsyntax/codemap.rs4
-rw-r--r--src/libsyntax/ext/base.rs108
-rw-r--r--src/libsyntax/ext/expand.rs3
-rw-r--r--src/libsyntax/ext/pipes/liveness.rs4
-rw-r--r--src/libsyntax/ext/pipes/proto.rs18
-rw-r--r--src/libsyntax/print/pp.rs4
-rw-r--r--src/libsyntax/print/pprust.rs8
-rw-r--r--src/libsyntax/util/interner.rs4
-rw-r--r--src/libsyntax/visit.rs6
-rw-r--r--src/test/compile-fail/access-mode-in-closures.rs2
-rw-r--r--src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs1
-rw-r--r--src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs1
-rw-r--r--src/test/compile-fail/attempted-access-non-fatal.rs4
-rw-r--r--src/test/compile-fail/borrowck-addr-of-upvar.rs4
-rw-r--r--src/test/compile-fail/borrowck-assign-comp-idx.rs10
-rw-r--r--src/test/compile-fail/borrowck-assign-comp.rs18
-rw-r--r--src/test/compile-fail/borrowck-assign-to-constants.rs4
-rw-r--r--src/test/compile-fail/borrowck-assign-to-enum.rs2
-rw-r--r--src/test/compile-fail/borrowck-assign-to-subfield.rs2
-rw-r--r--src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs (renamed from src/test/compile-fail/auto-ref-borrowck-failure.rs)10
-rw-r--r--src/test/compile-fail/borrowck-autoref-3261.rs4
-rw-r--r--src/test/compile-fail/borrowck-bad-nested-calls-free.rs43
-rw-r--r--src/test/compile-fail/borrowck-bad-nested-calls-move.rs43
-rw-r--r--src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs70
-rw-r--r--src/test/compile-fail/borrowck-borrow-from-stack-variable.rs67
-rw-r--r--src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs3
-rw-r--r--src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs2
-rw-r--r--src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs8
-rw-r--r--src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs4
-rw-r--r--src/test/compile-fail/borrowck-insert-during-each.rs4
-rw-r--r--src/test/compile-fail/borrowck-issue-2657-1.rs4
-rw-r--r--src/test/compile-fail/borrowck-issue-2657-2.rs2
-rw-r--r--src/test/compile-fail/borrowck-lend-flow-if.rs52
-rw-r--r--src/test/compile-fail/borrowck-lend-flow-loop.rs164
-rw-r--r--src/test/compile-fail/borrowck-lend-flow-match.rs60
-rw-r--r--src/test/compile-fail/borrowck-lend-flow.rs93
-rw-r--r--src/test/compile-fail/borrowck-loan-blocks-move-cc.rs8
-rw-r--r--src/test/compile-fail/borrowck-loan-blocks-move.rs4
-rw-r--r--src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs4
-rw-r--r--src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs4
-rw-r--r--src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs6
-rw-r--r--src/test/compile-fail/borrowck-loan-rcvr.rs24
-rw-r--r--src/test/compile-fail/borrowck-loan-vec-content.rs4
-rw-r--r--src/test/compile-fail/borrowck-move-by-capture.rs2
-rw-r--r--src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs2
-rw-r--r--src/test/compile-fail/borrowck-mut-boxed-vec.rs4
-rw-r--r--src/test/compile-fail/borrowck-mut-deref-comp.rs4
-rw-r--r--src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs2
-rw-r--r--src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs4
-rw-r--r--src/test/compile-fail/borrowck-pat-by-value-binding.rs11
-rw-r--r--src/test/compile-fail/borrowck-pat-enum.rs14
-rw-r--r--src/test/compile-fail/borrowck-pat-reassign-binding.rs11
-rw-r--r--src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs26
-rw-r--r--src/test/compile-fail/borrowck-reborrow-from-mut.rs22
-rw-r--r--src/test/compile-fail/borrowck-ref-into-rvalue.rs8
-rw-r--r--src/test/compile-fail/borrowck-ref-mut-of-imm.rs2
-rw-r--r--src/test/compile-fail/borrowck-unary-move-2.rs2
-rw-r--r--src/test/compile-fail/borrowck-unary-move.rs4
-rw-r--r--src/test/compile-fail/borrowck-uniq-via-box.rs55
-rw-r--r--src/test/compile-fail/borrowck-uniq-via-lend.rs8
-rw-r--r--src/test/compile-fail/borrowck-uniq-via-ref.rs7
-rw-r--r--src/test/compile-fail/borrowck-vec-pattern-element-loan.rs12
-rw-r--r--src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs2
-rw-r--r--src/test/compile-fail/borrowck-vec-pattern-move-tail.rs5
-rw-r--r--src/test/compile-fail/borrowck-vec-pattern-nesting.rs4
-rw-r--r--src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs4
-rw-r--r--src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs4
-rw-r--r--src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs4
-rw-r--r--src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs4
-rw-r--r--src/test/compile-fail/borrowck-wg-move-base-2.rs2
-rw-r--r--src/test/compile-fail/fn-variance-3.rs2
-rw-r--r--src/test/compile-fail/immut-function-arguments.rs4
-rw-r--r--src/test/compile-fail/index_message.rs2
-rw-r--r--src/test/compile-fail/issue-1896-1.rs4
-rw-r--r--src/test/compile-fail/issue-2149.rs1
-rw-r--r--src/test/compile-fail/issue-2151.rs3
-rw-r--r--src/test/compile-fail/issue-2590.rs2
-rw-r--r--src/test/compile-fail/issue-3044.rs1
-rw-r--r--src/test/compile-fail/issue-511.rs2
-rw-r--r--src/test/compile-fail/kindck-owned-trait-contains.rs3
-rw-r--r--src/test/compile-fail/lambda-mutate-nested.rs2
-rw-r--r--src/test/compile-fail/lambda-mutate.rs2
-rw-r--r--src/test/compile-fail/moves-based-on-type-block-bad.rs2
-rw-r--r--src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs2
-rw-r--r--src/test/compile-fail/mutable-class-fields-2.rs3
-rw-r--r--src/test/compile-fail/mutable-class-fields.rs5
-rw-r--r--src/test/compile-fail/mutable-huh-ptr-assign.rs2
-rw-r--r--src/test/compile-fail/regions-addr-of-arg.rs2
-rw-r--r--src/test/compile-fail/regions-creating-enums.rs4
-rw-r--r--src/test/compile-fail/regions-creating-enums4.rs3
-rw-r--r--src/test/compile-fail/regions-escape-bound-fn.rs2
-rw-r--r--src/test/compile-fail/regions-escape-loop-via-variable.rs2
-rw-r--r--src/test/compile-fail/regions-escape-loop-via-vec.rs4
-rw-r--r--src/test/compile-fail/regions-escape-via-trait-or-not.rs7
-rw-r--r--src/test/compile-fail/regions-infer-borrow-scope-too-big.rs2
-rw-r--r--src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs2
-rw-r--r--src/test/compile-fail/regions-nested-fns-2.rs2
-rw-r--r--src/test/compile-fail/regions-nested-fns.rs2
-rw-r--r--src/test/compile-fail/regions-ret-borrowed-1.rs1
-rw-r--r--src/test/compile-fail/regions-ret-borrowed.rs1
-rw-r--r--src/test/compile-fail/regions-ret.rs2
-rw-r--r--src/test/compile-fail/regions-var-type-out-of-scope.rs2
-rw-r--r--src/test/compile-fail/swap-no-lval.rs4
-rw-r--r--src/test/compile-fail/writing-to-immutable-vec.rs6
-rw-r--r--src/test/run-pass/borrowck-nested-calls.rs (renamed from src/test/compile-fail/issue-4500.rs)24
-rw-r--r--src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs6
-rw-r--r--src/test/run-pass/issue-2735-2.rs18
-rw-r--r--src/test/run-pass/issue-2735-3.rs18
172 files changed, 6449 insertions, 4277 deletions
diff --git a/src/libcore/cleanup.rs b/src/libcore/cleanup.rs
index a07c6b4811b..1dfe1b22dc4 100644
--- a/src/libcore/cleanup.rs
+++ b/src/libcore/cleanup.rs
@@ -126,14 +126,17 @@ struct AnnihilateStats {
     n_bytes_freed: uint
 }
 
-unsafe fn each_live_alloc(f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) {
+unsafe fn each_live_alloc(read_next_before: bool,
+                          f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) {
+    //! Walks the internal list of allocations
+
     use managed;
 
     let task: *Task = transmute(rustrt::rust_get_task());
     let box = (*task).boxed_region.live_allocs;
     let mut box: *mut BoxRepr = transmute(copy box);
     while box != mut_null() {
-        let next = transmute(copy (*box).header.next);
+        let next_before = transmute(copy (*box).header.next);
         let uniq =
             (*box).header.ref_count == managed::raw::RC_MANAGED_UNIQUE;
 
@@ -141,7 +144,11 @@ unsafe fn each_live_alloc(f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) {
             break
         }
 
-        box = next
+        if read_next_before {
+            box = next_before;
+        } else {
+            box = transmute(copy (*box).header.next);
+        }
     }
 }
 
@@ -159,7 +166,7 @@ fn debug_mem() -> bool {
 #[cfg(notest)]
 #[lang="annihilate"]
 pub unsafe fn annihilate() {
-    use unstable::lang::local_free;
+    use unstable::lang::{local_free, debug_ptr};
     use io::WriterUtil;
     use io;
     use libc;
@@ -173,27 +180,46 @@ pub unsafe fn annihilate() {
     };
 
     // Pass 1: Make all boxes immortal.
-    for each_live_alloc |box, uniq| {
+    //
+    // In this pass, nothing gets freed, so it does not matter whether
+    // we read the next field before or after the callback.
+    for each_live_alloc(true) |box, uniq| {
         stats.n_total_boxes += 1;
         if uniq {
+            debug_ptr("Managed-uniq: ", &*box);
             stats.n_unique_boxes += 1;
         } else {
+            debug_ptr("Immortalizing: ", &*box);
             (*box).header.ref_count = managed::raw::RC_IMMORTAL;
         }
     }
 
     // Pass 2: Drop all boxes.
-    for each_live_alloc |box, uniq| {
+    //
+    // In this pass, unique-managed boxes may get freed, but not
+    // managed boxes, so we must read the `next` field *after* the
+    // callback, as the original value may have been freed.
+    for each_live_alloc(false) |box, uniq| {
         if !uniq {
+            debug_ptr("Invoking tydesc/glue on: ", &*box);
             let tydesc: *TypeDesc = transmute(copy (*box).header.type_desc);
             let drop_glue: DropGlue = transmute(((*tydesc).drop_glue, 0));
+            debug_ptr("Box data: ", &(*box).data);
+            debug_ptr("Type descriptor: ", tydesc);
             drop_glue(to_unsafe_ptr(&tydesc), transmute(&(*box).data));
+            debug_ptr("Dropped ", &*box);
         }
     }
 
     // Pass 3: Free all boxes.
-    for each_live_alloc |box, uniq| {
+    //
+    // In this pass, managed boxes may get freed (but not
+    // unique-managed boxes, though I think that none of those are
+    // left), so we must read the `next` field before, since it will
+    // not be valid after.
+    for each_live_alloc(true) |box, uniq| {
         if !uniq {
+            debug_ptr("About to free: ", &*box);
             stats.n_bytes_freed +=
                 (*((*box).header.type_desc)).size
                 + sys::size_of::<BoxRepr>();
diff --git a/src/libcore/io.rs b/src/libcore/io.rs
index 35ffd88c8f4..217ea1a9982 100644
--- a/src/libcore/io.rs
+++ b/src/libcore/io.rs
@@ -1022,7 +1022,7 @@ pub enum WriterType { Screen, File }
 pub trait Writer {
 
     /// Write all of the given bytes.
-    fn write(&self, v: &const [u8]);
+    fn write(&self, v: &[u8]);
 
     /// Move the current position within the stream. The second parameter
     /// determines the position that the first parameter is relative to.
@@ -1039,7 +1039,7 @@ pub trait Writer {
 }
 
 impl Writer for @Writer {
-    fn write(&self, v: &const [u8]) { self.write(v) }
+    fn write(&self, v: &[u8]) { self.write(v) }
     fn seek(&self, a: int, b: SeekStyle) { self.seek(a, b) }
     fn tell(&self) -> uint { self.tell() }
     fn flush(&self) -> int { self.flush() }
@@ -1047,7 +1047,7 @@ impl Writer for @Writer {
 }
 
 impl<W:Writer,C> Writer for Wrapper<W, C> {
-    fn write(&self, bs: &const [u8]) { self.base.write(bs); }
+    fn write(&self, bs: &[u8]) { self.base.write(bs); }
     fn seek(&self, off: int, style: SeekStyle) { self.base.seek(off, style); }
     fn tell(&self) -> uint { self.base.tell() }
     fn flush(&self) -> int { self.base.flush() }
@@ -1055,7 +1055,7 @@ impl<W:Writer,C> Writer for Wrapper<W, C> {
 }
 
 impl Writer for *libc::FILE {
-    fn write(&self, v: &const [u8]) {
+    fn write(&self, v: &[u8]) {
         unsafe {
             do vec::as_const_buf(v) |vbuf, len| {
                 let nout = libc::fwrite(vbuf as *c_void,
@@ -1105,7 +1105,7 @@ pub fn FILE_writer(f: *libc::FILE, cleanup: bool) -> @Writer {
 }
 
 impl Writer for fd_t {
-    fn write(&self, v: &const [u8]) {
+    fn write(&self, v: &[u8]) {
         unsafe {
             let mut count = 0u;
             do vec::as_const_buf(v) |vbuf, len| {
@@ -1262,7 +1262,7 @@ pub fn u64_to_be_bytes<T>(n: u64, size: uint,
     }
 }
 
-pub fn u64_from_be_bytes(data: &const [u8],
+pub fn u64_from_be_bytes(data: &[u8],
                          start: uint,
                          size: uint)
                       -> u64 {
@@ -1497,7 +1497,7 @@ pub struct BytesWriter {
 }
 
 impl Writer for BytesWriter {
-    fn write(&self, v: &const [u8]) {
+    fn write(&self, v: &[u8]) {
         let v_len = v.len();
         let bytes_len = vec::uniq_len(&const self.bytes);
 
diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs
index 28946281628..a2132676c1a 100644
--- a/src/libcore/rt/sched/mod.rs
+++ b/src/libcore/rt/sched/mod.rs
@@ -304,7 +304,7 @@ pub impl Scheduler {
         unsafe {
             let last_task = transmute::<Option<&Task>, Option<&mut Task>>(last_task);
             let last_task_context = match last_task {
-                Some(ref t) => Some(&mut t.saved_context), None => None
+                Some(t) => Some(&mut t.saved_context), None => None
             };
             let next_task_context = match self.current_task {
                 Some(ref mut t) => Some(&mut t.saved_context), None => None
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 064bffa0056..f4430ca669f 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -2356,9 +2356,6 @@ pub trait StrSlice<'self> {
     fn any(&self, it: &fn(char) -> bool) -> bool;
     fn contains<'a>(&self, needle: &'a str) -> bool;
     fn contains_char(&self, needle: char) -> bool;
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    #[cfg(stage3)]
     fn char_iter(&self) -> StrCharIterator<'self>;
     fn each(&self, it: &fn(u8) -> bool);
     fn eachi(&self, it: &fn(uint, u8) -> bool);
@@ -2420,9 +2417,6 @@ impl<'self> StrSlice<'self> for &'self str {
         contains_char(*self, needle)
     }
 
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    #[cfg(stage3)]
     #[inline]
     fn char_iter(&self) -> StrCharIterator<'self> {
         StrCharIterator {
@@ -2615,17 +2609,11 @@ impl Clone for ~str {
     }
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 pub struct StrCharIterator<'self> {
     priv index: uint,
     priv string: &'self str,
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 impl<'self> Iterator<char> for StrCharIterator<'self> {
     #[inline]
     fn next(&mut self) -> Option<char> {
diff --git a/src/libcore/to_bytes.rs b/src/libcore/to_bytes.rs
index 7b4b6994e50..63dcf0f44dc 100644
--- a/src/libcore/to_bytes.rs
+++ b/src/libcore/to_bytes.rs
@@ -19,7 +19,7 @@ use io::Writer;
 use option::{None, Option, Some};
 use str;
 
-pub type Cb<'self> = &'self fn(buf: &const [u8]) -> bool;
+pub type Cb<'self> = &'self fn(buf: &[u8]) -> bool;
 
 /**
  * A trait to implement in order to make a type hashable;
diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs
index de0542afc39..cf71b01aeae 100644
--- a/src/libcore/unstable/lang.rs
+++ b/src/libcore/unstable/lang.rs
@@ -11,7 +11,7 @@
 //! Runtime calls emitted by the compiler.
 
 use cast::transmute;
-use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int};
+use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO};
 use managed::raw::BoxRepr;
 use str;
 use sys;
@@ -74,7 +74,44 @@ pub fn fail_borrowed() {
 #[lang="exchange_malloc"]
 #[inline(always)]
 pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
-    transmute(exchange_alloc::malloc(transmute(td), transmute(size)))
+    let result = transmute(exchange_alloc::malloc(transmute(td), transmute(size)));
+    debug_ptr("exchange_malloc: ", result);
+    return result;
+}
+
+/// Because this code is so perf. sensitive, use a static constant so that
+/// debug printouts are compiled out most of the time.
+static ENABLE_DEBUG_PTR: bool = false;
+
+#[inline]
+pub fn debug_ptr<T>(tag: &'static str, p: *T) {
+    //! A useful debugging function that prints a pointer + tag + newline
+    //! without allocating memory.
+
+    if ENABLE_DEBUG_PTR && ::rt::env::get().debug_mem {
+        debug_ptr_slow(tag, p);
+    }
+
+    fn debug_ptr_slow<T>(tag: &'static str, p: *T) {
+        use io;
+        let dbg = STDERR_FILENO as io::fd_t;
+        let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
+                       '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+        dbg.write_str(tag);
+
+        static uint_nibbles: uint = ::uint::bytes << 1;
+        let mut buffer = [0_u8, ..uint_nibbles+1];
+        let mut i = p as uint;
+        let mut c = uint_nibbles;
+        while c > 0 {
+            c -= 1;
+            buffer[c] = letters[i & 0xF] as u8;
+            i >>= 4;
+        }
+        dbg.write(buffer.slice(0, uint_nibbles));
+
+        dbg.write_str("\n");
+    }
 }
 
 // NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
@@ -83,13 +120,16 @@ pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
 #[lang="exchange_free"]
 #[inline(always)]
 pub unsafe fn exchange_free(ptr: *c_char) {
+    debug_ptr("exchange_free: ", ptr);
     exchange_alloc::free(transmute(ptr))
 }
 
 #[lang="malloc"]
 #[inline(always)]
 pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
-    return rustrt::rust_upcall_malloc_noswitch(td, size);
+    let result = rustrt::rust_upcall_malloc_noswitch(td, size);
+    debug_ptr("local_malloc: ", result);
+    return result;
 }
 
 // NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
@@ -98,6 +138,7 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
 #[lang="free"]
 #[inline(always)]
 pub unsafe fn local_free(ptr: *c_char) {
+    debug_ptr("local_free: ", ptr);
     rustrt::rust_upcall_free_noswitch(ptr);
 }
 
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 94f86664353..2f9488d1bc7 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -19,9 +19,6 @@ use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater};
 use clone::Clone;
 use old_iter::BaseIter;
 use old_iter;
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 use iterator::Iterator;
 use kinds::Copy;
 use libc;
@@ -1824,7 +1821,7 @@ pub trait CopyableVector<T> {
 }
 
 /// Extension methods for vectors
-impl<'self,T:Copy> CopyableVector<T> for &'self const [T] {
+impl<'self,T:Copy> CopyableVector<T> for &'self [T] {
     /// Returns a copy of `v`.
     #[inline]
     fn to_owned(&self) -> ~[T] {
@@ -2710,18 +2707,12 @@ impl<A:Clone> Clone for ~[A] {
 }
 
 // could be implemented with &[T] with .slice(), but this avoids bounds checks
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 pub struct VecIterator<'self, T> {
     priv ptr: *T,
     priv end: *T,
     priv lifetime: &'self T // FIXME: #5922
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 impl<'self, T> Iterator<&'self T> for VecIterator<'self, T> {
     #[inline]
     fn next(&mut self) -> Option<&'self T> {
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 2e64c0c45bf..e899b1abc26 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -263,7 +263,7 @@ pub fn compile_rest(sess: Session,
              middle::check_loop::check_crate(ty_cx, crate));
 
         let middle::moves::MoveMaps {moves_map, variable_moves_map,
-                                     capture_map} =
+                                     moved_variables_set, capture_map} =
             time(time_passes, ~"compute moves", ||
                  middle::moves::compute_moves(ty_cx, method_map, crate));
 
@@ -271,20 +271,19 @@ pub fn compile_rest(sess: Session,
              middle::check_match::check_crate(ty_cx, method_map,
                                               moves_map, crate));
 
-        let last_use_map =
-            time(time_passes, ~"liveness checking", ||
-                 middle::liveness::check_crate(ty_cx, method_map,
-                                               variable_moves_map,
-                                               capture_map, crate));
+        time(time_passes, ~"liveness checking", ||
+             middle::liveness::check_crate(ty_cx, method_map,
+                                           variable_moves_map,
+                                           capture_map, crate));
 
-        let (root_map, mutbl_map, write_guard_map) =
+        let (root_map, write_guard_map) =
             time(time_passes, ~"borrow checking", ||
                  middle::borrowck::check_crate(ty_cx, method_map,
-                                               moves_map, capture_map,
-                                               crate));
+                                               moves_map, moved_variables_set,
+                                               capture_map, crate));
 
         time(time_passes, ~"kind checking", ||
-             kind::check_crate(ty_cx, method_map, last_use_map, crate));
+             kind::check_crate(ty_cx, method_map, crate));
 
         time(time_passes, ~"lint checking", ||
              lint::check_crate(ty_cx, crate));
@@ -292,9 +291,7 @@ pub fn compile_rest(sess: Session,
         if upto == cu_no_trans { return (crate, Some(ty_cx)); }
 
         let maps = astencode::Maps {
-            mutbl_map: mutbl_map,
             root_map: root_map,
-            last_use_map: last_use_map,
             method_map: method_map,
             vtable_map: vtable_map,
             write_guard_map: write_guard_map,
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index 55c81e6d17b..fff97d2436a 100644
--- a/src/librustc/driver/session.rs
+++ b/src/librustc/driver/session.rs
@@ -173,15 +173,19 @@ pub type Session = @Session_;
 
 pub impl Session_ {
     fn span_fatal(@self, sp: span, msg: ~str) -> ! {
+        debug!("span_fatal invoked: %s", msg);
         self.span_diagnostic.span_fatal(sp, msg)
     }
     fn fatal(@self, msg: ~str) -> ! {
+        debug!("fatal invoked: %s", msg);
         self.span_diagnostic.handler().fatal(msg)
     }
     fn span_err(@self, sp: span, msg: ~str) {
+        debug!("span_err invoked: %s", msg);
         self.span_diagnostic.span_err(sp, msg)
     }
     fn err(@self, msg: ~str) {
+        debug!("err invoked: %s", msg);
         self.span_diagnostic.handler().err(msg)
     }
     fn has_errors(@self) -> bool {
@@ -191,15 +195,19 @@ pub impl Session_ {
         self.span_diagnostic.handler().abort_if_errors()
     }
     fn span_warn(@self, sp: span, msg: ~str) {
+        debug!("span_warn invoked: %s", msg);
         self.span_diagnostic.span_warn(sp, msg)
     }
     fn warn(@self, msg: ~str) {
+        debug!("warn invoked: %s", msg);
         self.span_diagnostic.handler().warn(msg)
     }
     fn span_note(@self, sp: span, msg: ~str) {
+        debug!("span_note invoked: %s", msg);
         self.span_diagnostic.span_note(sp, msg)
     }
     fn note(@self, msg: ~str) {
+        debug!("note invoked: %s", msg);
         self.span_diagnostic.handler().note(msg)
     }
     fn span_bug(@self, sp: span, msg: ~str) -> ! {
diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs
index 02e2a4c8734..22bce62336c 100644
--- a/src/librustc/front/test.rs
+++ b/src/librustc/front/test.rs
@@ -69,7 +69,8 @@ fn generate_test_harness(sess: session::Session,
         testfns: ~[]
     };
 
-    cx.ext_cx.bt_push(ExpandedFrom(CallInfo {
+    let ext_cx = cx.ext_cx;
+    ext_cx.bt_push(ExpandedFrom(CallInfo {
         call_site: dummy_sp(),
         callee: NameAndSpan {
             name: ~"test",
@@ -84,7 +85,7 @@ fn generate_test_harness(sess: session::Session,
 
     let fold = fold::make_fold(precursor);
     let res = @fold.fold_crate(&*crate);
-    cx.ext_cx.bt_pop();
+    ext_cx.bt_pop();
     return res;
 }
 
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index cfe31360d32..1a94b57279c 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -244,8 +244,8 @@ fn doc_transformed_self_ty(doc: ebml::Doc,
     }
 }
 
-pub fn item_type(_: ast::def_id, item: ebml::Doc, tcx: ty::ctxt, cdata: cmd)
-                 -> ty::t {
+pub fn item_type(_item_id: ast::def_id, item: ebml::Doc,
+                 tcx: ty::ctxt, cdata: cmd) -> ty::t {
     doc_type(item, tcx, cdata)
 }
 
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index 011ee115e8c..963afa08bfe 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -245,6 +245,9 @@ fn parse_region(st: @mut PState) -> ty::Region {
       't' => {
         ty::re_static
       }
+      'e' => {
+        ty::re_static
+      }
       _ => fail!(~"parse_region: bad input")
     }
 }
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 763b1984b81..fdba3ac4f00 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -71,30 +71,29 @@ pub fn enc_ty(w: @io::Writer, cx: @ctxt, t: ty::t) {
         w.write_str(result_str);
       }
       ac_use_abbrevs(abbrevs) => {
-        match abbrevs.find(&t) {
-          Some(a) => { w.write_str(*a.s); return; }
-          None => {
-            let pos = w.tell();
-            enc_sty(w, cx, /*bad*/copy ty::get(t).sty);
-            let end = w.tell();
-            let len = end - pos;
-            fn estimate_sz(u: uint) -> uint {
-                let mut n = u;
-                let mut len = 0u;
-                while n != 0u { len += 1u; n = n >> 4u; }
-                return len;
-            }
-            let abbrev_len = 3u + estimate_sz(pos) + estimate_sz(len);
-            if abbrev_len < len {
-                // I.e. it's actually an abbreviation.
-                let s = ~"#" + uint::to_str_radix(pos, 16u) + ~":" +
-                    uint::to_str_radix(len, 16u) + ~"#";
-                let a = ty_abbrev { pos: pos, len: len, s: @s };
-                abbrevs.insert(t, a);
-            }
-            return;
+          match abbrevs.find(&t) {
+              Some(a) => { w.write_str(*a.s); return; }
+              None => {}
           }
-        }
+          let pos = w.tell();
+          enc_sty(w, cx, /*bad*/copy ty::get(t).sty);
+          let end = w.tell();
+          let len = end - pos;
+          fn estimate_sz(u: uint) -> uint {
+              let mut n = u;
+              let mut len = 0u;
+              while n != 0u { len += 1u; n = n >> 4u; }
+              return len;
+          }
+          let abbrev_len = 3u + estimate_sz(pos) + estimate_sz(len);
+          if abbrev_len < len {
+              // I.e. it's actually an abbreviation.
+              let s = ~"#" + uint::to_str_radix(pos, 16u) + ~":" +
+                  uint::to_str_radix(len, 16u) + ~"#";
+              let a = ty_abbrev { pos: pos, len: len, s: @s };
+              abbrevs.insert(t, a);
+          }
+          return;
       }
     }
 }
@@ -152,6 +151,9 @@ fn enc_region(w: @io::Writer, cx: @ctxt, r: ty::Region) {
       ty::re_static => {
         w.write_char('t');
       }
+      ty::re_empty => {
+        w.write_char('e');
+      }
       ty::re_infer(_) => {
         // these should not crop up after typeck
         cx.diag.handler().bug(~"Cannot encode region variables");
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index c65521228fa..7a3bdce875d 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -44,9 +44,7 @@ use writer = std::ebml::writer;
 
 // Auxiliary maps of things to be encoded
 pub struct Maps {
-    mutbl_map: middle::borrowck::mutbl_map,
     root_map: middle::borrowck::root_map,
-    last_use_map: middle::liveness::last_use_map,
     method_map: middle::typeck::method_map,
     vtable_map: middle::typeck::vtable_map,
     write_guard_map: middle::borrowck::write_guard_map,
@@ -151,7 +149,7 @@ pub fn decode_inlined_item(cdata: @cstore::crate_metadata,
 fn reserve_id_range(sess: Session,
                     from_id_range: ast_util::id_range) -> ast_util::id_range {
     // Handle the case of an empty range:
-    if ast_util::empty(from_id_range) { return from_id_range; }
+    if from_id_range.empty() { return from_id_range; }
     let cnt = from_id_range.max - from_id_range.min;
     let to_id_min = sess.parse_sess.next_id;
     let to_id_max = sess.parse_sess.next_id + cnt;
@@ -162,7 +160,6 @@ fn reserve_id_range(sess: Session,
 pub impl ExtendedDecodeContext {
     fn tr_id(&self, id: ast::node_id) -> ast::node_id {
         /*!
-         *
          * Translates an internal id, meaning a node id that is known
          * to refer to some part of the item currently being inlined,
          * such as a local variable or argument.  All naked node-ids
@@ -173,12 +170,11 @@ pub impl ExtendedDecodeContext {
          */
 
         // from_id_range should be non-empty
-        assert!(!ast_util::empty(self.from_id_range));
+        assert!(!self.from_id_range.empty());
         (id - self.from_id_range.min + self.to_id_range.min)
     }
     fn tr_def_id(&self, did: ast::def_id) -> ast::def_id {
         /*!
-         *
          * Translates an EXTERNAL def-id, converting the crate number
          * from the one used in the encoded data to the current crate
          * numbers..  By external, I mean that it be translated to a
@@ -203,7 +199,6 @@ pub impl ExtendedDecodeContext {
     }
     fn tr_intern_def_id(&self, did: ast::def_id) -> ast::def_id {
         /*!
-         *
          * Translates an INTERNAL def-id, meaning a def-id that is
          * known to refer to some part of the item currently being
          * inlined.  In that case, we want to convert the def-id to
@@ -461,11 +456,7 @@ impl tr for ty::AutoAdjustment {
 
 impl tr for ty::AutoRef {
     fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::AutoRef {
-        ty::AutoRef {
-            kind: self.kind,
-            region: self.region.tr(xcx),
-            mutbl: self.mutbl,
-        }
+        self.map_region(|r| r.tr(xcx))
     }
 }
 
@@ -474,7 +465,7 @@ impl tr for ty::Region {
         match *self {
             ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
             ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
-            ty::re_static | ty::re_infer(*) => *self,
+            ty::re_empty | ty::re_static | ty::re_infer(*) => *self,
             ty::re_free(ref fr) => {
                 ty::re_free(ty::FreeRegion {scope_id: xcx.tr_id(fr.scope_id),
                                             bound_region: fr.bound_region.tr(xcx)})
@@ -914,23 +905,6 @@ fn encode_side_tables_for_id(ecx: @e::EncodeContext,
         }
     }
 
-    if maps.mutbl_map.contains(&id) {
-        do ebml_w.tag(c::tag_table_mutbl) {
-            ebml_w.id(id);
-        }
-    }
-
-    for maps.last_use_map.find(&id).each |&m| {
-        do ebml_w.tag(c::tag_table_last_use) {
-            ebml_w.id(id);
-            do ebml_w.tag(c::tag_table_val) {
-                do ebml_w.emit_from_vec(/*bad*/ copy **m) |id| {
-                    id.encode(ebml_w);
-                }
-            }
-        }
-    }
-
     for maps.method_map.find(&id).each |&mme| {
         do ebml_w.tag(c::tag_table_method_map) {
             ebml_w.id(id);
@@ -1108,9 +1082,7 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext,
                 found for id %d (orig %d)",
                tag, id, id0);
 
-        if tag == (c::tag_table_mutbl as uint) {
-            dcx.maps.mutbl_map.insert(id);
-        } else if tag == (c::tag_table_moves_map as uint) {
+        if tag == (c::tag_table_moves_map as uint) {
             dcx.maps.moves_map.insert(id);
         } else {
             let val_doc = entry_doc.get(c::tag_table_val as uint);
@@ -1138,11 +1110,6 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext,
             } else if tag == (c::tag_table_param_defs as uint) {
                 let bounds = val_dsr.read_type_param_def(xcx);
                 dcx.tcx.ty_param_defs.insert(id, bounds);
-            } else if tag == (c::tag_table_last_use as uint) {
-                let ids = val_dsr.read_to_vec(|| {
-                    xcx.tr_id(val_dsr.read_int())
-                });
-                dcx.maps.last_use_map.insert(id, @mut ids);
             } else if tag == (c::tag_table_method_map as uint) {
                 dcx.maps.method_map.insert(
                     id,
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index 07b6c80d420..56eb57009ca 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -18,284 +18,143 @@
 // 4. moves do not affect things loaned out in any way
 
 use middle::moves;
-use middle::typeck::check::PurityState;
-use middle::borrowck::{Loan, bckerr, BorrowckCtxt, inherent_mutability};
-use middle::borrowck::{ReqMaps, root_map_key, save_and_restore_managed};
-use middle::borrowck::{MoveError, MoveOk, MoveFromIllegalCmt};
-use middle::borrowck::{MoveWhileBorrowed};
-use middle::mem_categorization::{cat_arg, cat_comp, cat_deref};
-use middle::mem_categorization::{cat_local, cat_rvalue, cat_self};
-use middle::mem_categorization::{cat_special, cmt, gc_ptr, loan_path, lp_arg};
-use middle::mem_categorization::{lp_comp, lp_deref, lp_local};
+use middle::borrowck::*;
+use mc = middle::mem_categorization;
 use middle::ty;
-use util::ppaux::ty_to_str;
-
+use util::ppaux::Repr;
 use core::hashmap::HashSet;
-use core::util::with;
-use syntax::ast::m_mutbl;
+use syntax::ast::{m_mutbl, m_imm, m_const};
 use syntax::ast;
 use syntax::ast_util;
-use syntax::codemap::span;
-use syntax::print::pprust;
 use syntax::visit;
+use syntax::codemap::span;
 
-struct CheckLoanCtxt {
+struct CheckLoanCtxt<'self> {
     bccx: @BorrowckCtxt,
-    req_maps: ReqMaps,
-
-    reported: HashSet<ast::node_id>,
-
-    declared_purity: @mut PurityState,
-    fn_args: @mut @~[ast::node_id]
-}
-
-// if we are enforcing purity, why are we doing so?
-#[deriving(Eq)]
-enum purity_cause {
-    // enforcing purity because fn was declared pure:
-    pc_pure_fn,
-
-    // enforce purity because we need to guarantee the
-    // validity of some alias; `bckerr` describes the
-    // reason we needed to enforce purity.
-    pc_cmt(bckerr)
-}
-
-// if we're not pure, why?
-#[deriving(Eq)]
-enum impurity_cause {
-    // some surrounding block was marked as 'unsafe'
-    pc_unsafe,
-
-    // nothing was unsafe, and nothing was pure
-    pc_default,
+    dfcx: &'self LoanDataFlow,
+    all_loans: &'self [Loan],
+    reported: @mut HashSet<ast::node_id>,
 }
 
 pub fn check_loans(bccx: @BorrowckCtxt,
-                   req_maps: ReqMaps,
-                   crate: @ast::crate) {
+                   dfcx: &LoanDataFlow,
+                   all_loans: &[Loan],
+                   body: &ast::blk) {
+    debug!("check_loans(body id=%?)", body.node.id);
+
     let clcx = @mut CheckLoanCtxt {
         bccx: bccx,
-        req_maps: req_maps,
-        reported: HashSet::new(),
-        declared_purity: @mut PurityState::function(ast::impure_fn, 0),
-        fn_args: @mut @~[]
+        dfcx: dfcx,
+        all_loans: all_loans,
+        reported: @mut HashSet::new(),
     };
+
     let vt = visit::mk_vt(@visit::Visitor {visit_expr: check_loans_in_expr,
                                            visit_local: check_loans_in_local,
                                            visit_block: check_loans_in_block,
+                                           visit_pat: check_loans_in_pat,
                                            visit_fn: check_loans_in_fn,
                                            .. *visit::default_visitor()});
-    visit::visit_crate(crate, clcx, vt);
+    (vt.visit_block)(body, clcx, vt);
 }
 
-#[deriving(Eq)]
-enum assignment_type {
-    at_straight_up,
-    at_swap
+enum MoveError {
+    MoveOk,
+    MoveFromIllegalCmt(mc::cmt),
+    MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/span)
 }
 
-pub impl assignment_type {
-    fn checked_by_liveness(&self) -> bool {
-        // the liveness pass guarantees that immutable local variables
-        // are only assigned once; but it doesn't consider &mut
-        match *self {
-          at_straight_up => true,
-          at_swap => true
-        }
-    }
-    fn ing_form(&self, desc: ~str) -> ~str {
-        match *self {
-          at_straight_up => ~"assigning to " + desc,
-          at_swap => ~"swapping to and from " + desc
-        }
-    }
-}
-
-pub impl CheckLoanCtxt {
+pub impl<'self> CheckLoanCtxt<'self> {
     fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
 
-    fn purity(&mut self, scope_id: ast::node_id)
-                -> Either<purity_cause, impurity_cause>
+    fn each_issued_loan(&self,
+                        scope_id: ast::node_id,
+                        op: &fn(&Loan) -> bool)
     {
-        let default_purity = match self.declared_purity.purity {
-          // an unsafe declaration overrides all
-          ast::unsafe_fn => return Right(pc_unsafe),
-
-          // otherwise, remember what was declared as the
-          // default, but we must scan for requirements
-          // imposed by the borrow check
-          ast::pure_fn => Left(pc_pure_fn),
-          ast::extern_fn | ast::impure_fn => Right(pc_default)
-        };
-
-        // scan to see if this scope or any enclosing scope requires
-        // purity.  if so, that overrides the declaration.
-
-        let mut scope_id = scope_id;
-        loop {
-            match self.req_maps.pure_map.find(&scope_id) {
-              None => (),
-              Some(e) => return Left(pc_cmt(*e))
-            }
-
-            match self.tcx().region_maps.opt_encl_scope(scope_id) {
-              None => return default_purity,
-              Some(next_scope_id) => scope_id = next_scope_id
+        //! Iterates over each loan that that has been issued
+        //! on entrance to `scope_id`, regardless of whether it is
+        //! actually *in scope* at that point.  Sometimes loans
+        //! are issued for future scopes and thus they may have been
+        //! *issued* but not yet be in effect.
+
+        for self.dfcx.each_bit_on_entry(scope_id) |loan_index| {
+            let loan = &self.all_loans[loan_index];
+            if !op(loan) {
+                return;
             }
         }
     }
 
-    fn walk_loans(&self,
-                  mut scope_id: ast::node_id,
-                  f: &fn(v: &Loan) -> bool) {
-
-        loop {
-            for self.req_maps.req_loan_map.find(&scope_id).each |loans| {
-                for loans.each |loan| {
-                    if !f(loan) { return; }
-                }
-            }
-
-            match self.tcx().region_maps.opt_encl_scope(scope_id) {
-              None => return,
-              Some(next_scope_id) => scope_id = next_scope_id,
-            }
-        }
-    }
-
-    fn walk_loans_of(&mut self,
-                     scope_id: ast::node_id,
-                     lp: @loan_path,
-                     f: &fn(v: &Loan) -> bool) {
-        for self.walk_loans(scope_id) |loan| {
-            if loan.lp == lp {
-                if !f(loan) { return; }
-            }
-        }
-    }
+    fn each_in_scope_loan(&self,
+                          scope_id: ast::node_id,
+                          op: &fn(&Loan) -> bool)
+    {
+        //! Like `each_issued_loan()`, but only considers loans that are
+        //! currently in scope.
 
-    // when we are in a pure context, we check each call to ensure
-    // that the function which is invoked is itself pure.
-    //
-    // note: we take opt_expr and expr_id separately because for
-    // overloaded operators the callee has an id but no expr.
-    // annoying.
-    fn check_pure_callee_or_arg(&mut self,
-                                pc: Either<purity_cause, impurity_cause>,
-                                opt_expr: Option<@ast::expr>,
-                                callee_id: ast::node_id,
-                                callee_span: span) {
-        let tcx = self.tcx();
-
-        debug!("check_pure_callee_or_arg(pc=%?, expr=%?, \
-                callee_id=%d, ty=%s)",
-               pc,
-               opt_expr.map(|e| pprust::expr_to_str(*e, tcx.sess.intr()) ),
-               callee_id,
-               ty_to_str(self.tcx(), ty::node_id_to_type(tcx, callee_id)));
-
-        // Purity rules: an expr B is a legal callee or argument to a
-        // call within a pure function A if at least one of the
-        // following holds:
-        //
-        // (a) A was declared pure and B is one of its arguments;
-        // (b) B is a stack closure;
-        // (c) B is a pure fn;
-        // (d) B is not a fn.
-
-        match opt_expr {
-          Some(expr) => {
-            match expr.node {
-              ast::expr_path(_) if pc == Left(pc_pure_fn) => {
-                let def = *self.tcx().def_map.get(&expr.id);
-                let did = ast_util::def_id_of_def(def);
-                let is_fn_arg =
-                    did.crate == ast::local_crate &&
-                    (*self.fn_args).contains(&(did.node));
-                if is_fn_arg { return; } // case (a) above
-              }
-              ast::expr_fn_block(*) | ast::expr_loop_body(*) |
-              ast::expr_do_body(*) => {
-                if self.is_stack_closure(expr.id) {
-                    // case (b) above
+        let region_maps = self.tcx().region_maps;
+        for self.each_issued_loan(scope_id) |loan| {
+            if region_maps.is_subscope_of(scope_id, loan.kill_scope) {
+                if !op(loan) {
                     return;
                 }
-              }
-              _ => ()
             }
-          }
-          None => ()
         }
+    }
 
-        let callee_ty = ty::node_id_to_type(tcx, callee_id);
-        match ty::get(callee_ty).sty {
-            ty::ty_bare_fn(ty::BareFnTy {purity: purity, _}) |
-            ty::ty_closure(ty::ClosureTy {purity: purity, _}) => {
-                match purity {
-                    ast::pure_fn => return, // case (c) above
-                    ast::impure_fn | ast::unsafe_fn | ast::extern_fn => {
-                        self.report_purity_error(
-                            pc, callee_span,
-                            fmt!("access to %s function",
-                                 purity.to_str()));
+    fn each_in_scope_restriction(&self,
+                                 scope_id: ast::node_id,
+                                 loan_path: @LoanPath,
+                                 op: &fn(&Loan, &Restriction) -> bool)
+    {
+        //! Iterates through all the in-scope restrictions for the
+        //! given `loan_path`
+
+        for self.each_in_scope_loan(scope_id) |loan| {
+            for loan.restrictions.each |restr| {
+                if restr.loan_path == loan_path {
+                    if !op(loan, restr) {
+                        return;
                     }
                 }
             }
-            _ => return, // case (d) above
         }
     }
 
-    // True if the expression with the given `id` is a stack closure.
-    // The expression must be an expr_fn_block(*)
-    fn is_stack_closure(&mut self, id: ast::node_id) -> bool {
-        let fn_ty = ty::node_id_to_type(self.tcx(), id);
-        match ty::get(fn_ty).sty {
-            ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil,
-                                          _}) => true,
-            _ => false
-        }
-    }
+    fn loans_generated_by(&self, scope_id: ast::node_id) -> ~[uint] {
+        //! Returns a vector of the loans that are generated as
+        //! we encounter `scope_id`.
 
-    fn is_allowed_pure_arg(&mut self, expr: @ast::expr) -> bool {
-        return match expr.node {
-          ast::expr_path(_) => {
-            let def = *self.tcx().def_map.get(&expr.id);
-            let did = ast_util::def_id_of_def(def);
-            did.crate == ast::local_crate &&
-                (*self.fn_args).contains(&(did.node))
-          }
-          ast::expr_fn_block(*) => self.is_stack_closure(expr.id),
-          _ => false,
-        };
+        let mut result = ~[];
+        for self.dfcx.each_gen_bit(scope_id) |loan_index| {
+            result.push(loan_index);
+        }
+        return result;
     }
 
     fn check_for_conflicting_loans(&mut self, scope_id: ast::node_id) {
-        debug!("check_for_conflicting_loans(scope_id=%?)", scope_id);
+        //! Checks to see whether any of the loans that are issued
+        //! by `scope_id` conflict with loans that have already been
+        //! issued when we enter `scope_id` (for example, we do not
+        //! permit two `&mut` borrows of the same variable).
 
-        let new_loans = match self.req_maps.req_loan_map.find(&scope_id) {
-            None => return,
-            Some(&loans) => loans
-        };
-        let new_loans: &mut ~[Loan] = new_loans;
-
-        debug!("new_loans has length %?", new_loans.len());
+        debug!("check_for_conflicting_loans(scope_id=%?)", scope_id);
 
-        let par_scope_id = self.tcx().region_maps.encl_scope(scope_id);
-        for self.walk_loans(par_scope_id) |old_loan| {
-            debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan));
+        let new_loan_indices = self.loans_generated_by(scope_id);
+        debug!("new_loan_indices = %?", new_loan_indices);
 
-            for new_loans.each |new_loan| {
-                self.report_error_if_loans_conflict(old_loan, new_loan);
+        for self.each_issued_loan(scope_id) |issued_loan| {
+            for new_loan_indices.each |&new_loan_index| {
+                let new_loan = &self.all_loans[new_loan_index];
+                self.report_error_if_loans_conflict(issued_loan, new_loan);
             }
         }
 
-        let len = new_loans.len();
-        for uint::range(0, len) |i| {
-            let loan_i = new_loans[i];
-            for uint::range(i+1, len) |j| {
-                let loan_j = new_loans[j];
-                self.report_error_if_loans_conflict(&loan_i, &loan_j);
+        for uint::range(0, new_loan_indices.len()) |i| {
+            let old_loan = &self.all_loans[new_loan_indices[i]];
+            for uint::range(i+1, new_loan_indices.len()) |j| {
+                let new_loan = &self.all_loans[new_loan_indices[j]];
+                self.report_error_if_loans_conflict(old_loan, new_loan);
             }
         }
     }
@@ -303,219 +162,358 @@ pub impl CheckLoanCtxt {
     fn report_error_if_loans_conflict(&self,
                                       old_loan: &Loan,
                                       new_loan: &Loan) {
-        if old_loan.lp != new_loan.lp {
-            return;
-        }
+        //! Checks whether `old_loan` and `new_loan` can safely be issued
+        //! simultaneously.
+
+        debug!("report_error_if_loans_conflict(old_loan=%s, new_loan=%s)",
+               old_loan.repr(self.tcx()),
+               new_loan.repr(self.tcx()));
+
+        // Should only be called for loans that are in scope at the same time.
+        let region_maps = self.tcx().region_maps;
+        assert!(region_maps.scopes_intersect(old_loan.kill_scope,
+                                             new_loan.kill_scope));
+
+        self.report_error_if_loan_conflicts_with_restriction(
+            old_loan, new_loan, old_loan, new_loan) &&
+        self.report_error_if_loan_conflicts_with_restriction(
+            new_loan, old_loan, old_loan, new_loan);
+    }
 
-        match (old_loan.kind, new_loan.kind) {
-            (PartialFreeze, PartialTake) | (PartialTake, PartialFreeze) |
-            (TotalFreeze, PartialFreeze) | (PartialFreeze, TotalFreeze) |
-            (Immobile, _) | (_, Immobile) |
-            (PartialFreeze, PartialFreeze) |
-            (PartialTake, PartialTake) |
-            (TotalFreeze, TotalFreeze) => {
-                /* ok */
-            }
+    fn report_error_if_loan_conflicts_with_restriction(&self,
+                                                       loan1: &Loan,
+                                                       loan2: &Loan,
+                                                       old_loan: &Loan,
+                                                       new_loan: &Loan) -> bool {
+        //! Checks whether the restrictions introduced by `loan1` would
+        //! prohibit `loan2`. Returns false if an error is reported.
+
+        debug!("report_error_if_loan_conflicts_with_restriction(\
+                loan1=%s, loan2=%s)",
+               loan1.repr(self.tcx()),
+               loan2.repr(self.tcx()));
+
+        // Restrictions that would cause the new loan to be immutable:
+        let illegal_if = match loan2.mutbl {
+            m_mutbl => RESTR_ALIAS | RESTR_FREEZE | RESTR_MUTATE,
+            m_imm =>   RESTR_ALIAS | RESTR_FREEZE,
+            m_const => RESTR_ALIAS,
+        };
+        debug!("illegal_if=%?", illegal_if);
+
+        for loan1.restrictions.each |restr| {
+            if !restr.set.intersects(illegal_if) { loop; }
+            if restr.loan_path != loan2.loan_path { loop; }
 
-            (PartialTake, TotalFreeze) | (TotalFreeze, PartialTake) |
-            (TotalTake, TotalFreeze) | (TotalFreeze, TotalTake) |
-            (TotalTake, PartialFreeze) | (PartialFreeze, TotalTake) |
-            (TotalTake, PartialTake) | (PartialTake, TotalTake) |
-            (TotalTake, TotalTake) => {
-                self.bccx.span_err(
-                    new_loan.cmt.span,
-                    fmt!("loan of %s as %s \
-                          conflicts with prior loan",
-                         self.bccx.cmt_to_str(new_loan.cmt),
-                         self.bccx.loan_kind_to_str(new_loan.kind)));
-                self.bccx.span_note(
-                    old_loan.cmt.span,
-                    fmt!("prior loan as %s granted here",
-                         self.bccx.loan_kind_to_str(old_loan.kind)));
+            match (new_loan.mutbl, old_loan.mutbl) {
+                (m_mutbl, m_mutbl) => {
+                    self.bccx.span_err(
+                        new_loan.span,
+                        fmt!("cannot borrow `%s` as mutable \
+                              more than once at at a time",
+                             self.bccx.loan_path_to_str(new_loan.loan_path)));
+                    self.bccx.span_note(
+                        old_loan.span,
+                        fmt!("second borrow of `%s` as mutable occurs here",
+                             self.bccx.loan_path_to_str(new_loan.loan_path)));
+                    return false;
+                }
+
+                _ => {
+                    self.bccx.span_err(
+                        new_loan.span,
+                        fmt!("cannot borrow `%s` as %s because \
+                              it is also borrowed as %s"
+                             self.bccx.loan_path_to_str(new_loan.loan_path),
+                             self.bccx.mut_to_str(new_loan.mutbl),
+                             self.bccx.mut_to_str(old_loan.mutbl)));
+                    self.bccx.span_note(
+                        old_loan.span,
+                        fmt!("second borrow of `%s` occurs here",
+                             self.bccx.loan_path_to_str(new_loan.loan_path)));
+                    return false;
+                }
             }
         }
+
+        true
     }
 
-    fn is_local_variable(&self, cmt: cmt) -> bool {
+    fn is_local_variable(&self, cmt: mc::cmt) -> bool {
         match cmt.cat {
-          cat_local(_) => true,
+          mc::cat_local(_) => true,
           _ => false
         }
     }
 
-    fn check_assignment(&mut self, at: assignment_type, ex: @ast::expr) {
+    fn check_assignment(&self, expr: @ast::expr) {
         // We don't use cat_expr() here because we don't want to treat
         // auto-ref'd parameters in overloaded operators as rvalues.
-        let cmt = match self.bccx.tcx.adjustments.find(&ex.id) {
-            None => self.bccx.cat_expr_unadjusted(ex),
-            Some(&adj) => self.bccx.cat_expr_autoderefd(ex, adj)
+        let cmt = match self.bccx.tcx.adjustments.find(&expr.id) {
+            None => self.bccx.cat_expr_unadjusted(expr),
+            Some(&adj) => self.bccx.cat_expr_autoderefd(expr, adj)
         };
 
-        debug!("check_assignment(cmt=%s)",
-               self.bccx.cmt_to_repr(cmt));
-
-        if self.is_local_variable(cmt) && at.checked_by_liveness() {
-            // liveness guarantees that immutable local variables
-            // are only assigned once
-        } else {
-            match cmt.mutbl {
-                McDeclared | McInherited => {
-                    // Ok, but if this loan is a mutable loan, then mark the
-                    // loan path (if it exists) as being used. This is similar
-                    // to the check performed in loan.rs in issue_loan(). This
-                    // type of use of mutable is different from issuing a loan,
-                    // however.
-                    for cmt.lp.each |lp| {
-                        for lp.node_id().each |&id| {
-                            self.tcx().used_mut_nodes.insert(id);
-                        }
-                    }
-                }
-                McReadOnly | McImmutable => {
+        debug!("check_assignment(cmt=%s)", cmt.repr(self.tcx()));
+
+        // check that the value being assigned is declared as mutable
+        // and report an error otherwise.
+        match cmt.mutbl {
+            mc::McDeclared => {
+                // OK
+            }
+            mc::McInherited => {
+                // OK, but we may have to add an entry to `used_mut_nodes`
+                mark_writes_through_upvars_as_used_mut(self, cmt);
+            }
+            mc::McReadOnly | mc::McImmutable => {
+                // Subtle: liveness guarantees that immutable local
+                // variables are only assigned once, so no need to
+                // report an error for an assignment to a local
+                // variable (note also that it is not legal to borrow
+                // for a local variable before it has been assigned
+                // for the first time).
+                if !self.is_local_variable(cmt) {
                     self.bccx.span_err(
-                        ex.span,
-                        at.ing_form(self.bccx.cmt_to_str(cmt)));
-                    return;
+                        expr.span,
+                        fmt!("cannot assign to %s %s"
+                             cmt.mutbl.to_user_str(),
+                             self.bccx.cmt_to_str(cmt)));
                 }
+                return;
             }
         }
 
-        // if this is a pure function, only loan-able state can be
-        // assigned, because it is uniquely tied to this function and
-        // is not visible from the outside
-        let purity = self.purity(ex.id);
-        match purity {
-          Right(_) => (),
-          Left(pc_cmt(_)) => {
-            // Subtle: Issue #3162.  If we are enforcing purity
-            // because there is a reference to aliasable, mutable data
-            // that we require to be immutable, we can't allow writes
-            // even to data owned by the current stack frame.  This is
-            // because that aliasable data might have been located on
-            // the current stack frame, we don't know.
-            self.report_purity_error(
-                purity,
-                ex.span,
-                at.ing_form(self.bccx.cmt_to_str(cmt)));
-          }
-          Left(pc_pure_fn) => {
-            if cmt.lp.is_none() {
-                self.report_purity_error(
-                    purity, ex.span,
-                    at.ing_form(self.bccx.cmt_to_str(cmt)));
-            }
-          }
+        if check_for_aliasable_mutable_writes(self, expr, cmt) {
+            check_for_assignment_to_restricted_or_frozen_location(
+                self, expr, cmt);
         }
 
-        // check for a conflicting loan as well, except in the case of
-        // taking a mutable ref.  that will create a loan of its own
-        // which will be checked for compat separately in
-        // check_for_conflicting_loans()
-        for cmt.lp.each |lp| {
-            self.check_for_loan_conflicting_with_assignment(
-                at, ex, cmt, *lp);
-        }
+        fn mark_writes_through_upvars_as_used_mut(self: &CheckLoanCtxt,
+                                                  cmt: mc::cmt) {
+            //! If the mutability of the `cmt` being written is inherited
+            //! from a local variable in another closure, liveness may
+            //! not have been able to detect that this variable's mutability
+            //! is important, so we must add the variable to the
+            //! `used_mut_nodes` table here. This is because liveness
+            //! does not consider closures.
+
+            let mut passed_upvar = false;
+            let mut cmt = cmt;
+            loop {
+                debug!("mark_writes_through_upvars_as_used_mut(cmt=%s)",
+                       cmt.repr(self.tcx()));
+                match cmt.cat {
+                    mc::cat_local(id) |
+                    mc::cat_arg(id, _) |
+                    mc::cat_self(id) => {
+                        if passed_upvar {
+                            self.tcx().used_mut_nodes.insert(id);
+                        }
+                        return;
+                    }
 
-        self.bccx.add_to_mutbl_map(cmt);
+                    mc::cat_stack_upvar(b) => {
+                        cmt = b;
+                        passed_upvar = true;
+                    }
 
-        // Check for and insert write guards as necessary.
-        self.add_write_guards_if_necessary(cmt);
-    }
+                    mc::cat_rvalue |
+                    mc::cat_static_item |
+                    mc::cat_implicit_self |
+                    mc::cat_copied_upvar(*) |
+                    mc::cat_deref(_, _, mc::unsafe_ptr(*)) |
+                    mc::cat_deref(_, _, mc::gc_ptr(*)) |
+                    mc::cat_deref(_, _, mc::region_ptr(*)) => {
+                        assert_eq!(cmt.mutbl, mc::McDeclared);
+                        return;
+                    }
 
-    fn add_write_guards_if_necessary(&mut self, cmt: cmt) {
-        match cmt.cat {
-            cat_deref(base, deref_count, ptr_kind) => {
-                self.add_write_guards_if_necessary(base);
-
-                match ptr_kind {
-                    gc_ptr(ast::m_mutbl) => {
-                        let key = root_map_key {
-                            id: base.id,
-                            derefs: deref_count
-                        };
-                        self.bccx.write_guard_map.insert(key);
+                    mc::cat_discr(b, _) |
+                    mc::cat_deref(b, _, mc::uniq_ptr(*)) => {
+                        assert_eq!(cmt.mutbl, mc::McInherited);
+                        cmt = b;
+                    }
+
+                    mc::cat_interior(b, _) => {
+                        if cmt.mutbl == mc::McInherited {
+                            cmt = b;
+                        } else {
+                            return; // field declared as mutable or some such
+                        }
                     }
-                    _ => {}
                 }
             }
-            cat_comp(base, _) => {
-                self.add_write_guards_if_necessary(base);
-            }
-            _ => {}
         }
-    }
 
-    fn check_for_loan_conflicting_with_assignment(&mut self,
-                                                  at: assignment_type,
-                                                  ex: @ast::expr,
-                                                  cmt: cmt,
-                                                  lp: @loan_path) {
-        for self.walk_loans_of(ex.id, lp) |loan| {
-            match loan.kind {
-                Immobile => { /* ok */ }
-                TotalFreeze | PartialFreeze |
-                TotalTake | PartialTake => {
-                    self.bccx.span_err(
-                        ex.span,
-                        fmt!("%s prohibited due to outstanding loan",
-                             at.ing_form(self.bccx.cmt_to_str(cmt))));
-                    self.bccx.span_note(
-                        loan.cmt.span,
-                        fmt!("loan of %s granted here",
-                             self.bccx.cmt_to_str(loan.cmt)));
-                    return;
+        fn check_for_aliasable_mutable_writes(self: &CheckLoanCtxt,
+                                              expr: @ast::expr,
+                                              cmt: mc::cmt) -> bool {
+            //! Safety checks related to writes to aliasable, mutable locations
+
+            let guarantor = cmt.guarantor();
+            match guarantor.cat {
+                mc::cat_deref(b, _, mc::region_ptr(m_mutbl, _)) => {
+                    // Statically prohibit writes to `&mut` when aliasable
+
+                    match b.freely_aliasable() {
+                        None => {}
+                        Some(cause) => {
+                            self.bccx.report_aliasability_violation(
+                                expr.span,
+                                MutabilityViolation,
+                                cause);
+                        }
+                    }
                 }
+
+                mc::cat_deref(base, deref_count, mc::gc_ptr(ast::m_mutbl)) => {
+                    // Dynamically check writes to `@mut`
+
+                    let key = root_map_key {
+                        id: base.id,
+                        derefs: deref_count
+                    };
+                    self.bccx.write_guard_map.insert(key);
+                }
+
+                _ => {}
             }
-        }
 
-        // Subtle: if the mutability of the component being assigned
-        // is inherited from the thing that the component is embedded
-        // within, then we have to check whether that thing has been
-        // loaned out as immutable!  An example:
-        //    let mut x = {f: Some(3)};
-        //    let y = &x; // x loaned out as immutable
-        //    x.f = none; // changes type of y.f, which appears to be imm
-        match *lp {
-          lp_comp(lp_base, ck) if inherent_mutability(ck) != m_mutbl => {
-            self.check_for_loan_conflicting_with_assignment(
-                at, ex, cmt, lp_base);
-          }
-          lp_comp(*) | lp_self | lp_local(*) | lp_arg(*) | lp_deref(*) => ()
+            return true; // no errors reported
         }
-    }
 
-    fn report_purity_error(&mut self, pc: Either<purity_cause, impurity_cause>,
-                           sp: span, msg: ~str) {
-        match pc {
-          Right(pc_default) => { fail!(~"pc_default should be filtered sooner") }
-          Right(pc_unsafe) => {
-            // this error was prevented by being marked as unsafe, so flag the
-            // definition as having contributed to the validity of the program
-            let def = self.declared_purity.def;
-            debug!("flagging %? as a used unsafe source", def);
-            self.tcx().used_unsafe.insert(def);
-          }
-          Left(pc_pure_fn) => {
-            self.tcx().sess.span_err(
-                sp,
-                fmt!("%s prohibited in pure context", msg));
-          }
-          Left(pc_cmt(ref e)) => {
-            if self.reported.insert((*e).cmt.id) {
-                self.tcx().sess.span_err(
-                    (*e).cmt.span,
-                    fmt!("illegal borrow unless pure: %s",
-                         self.bccx.bckerr_to_str((*e))));
-                self.bccx.note_and_explain_bckerr((*e));
-                self.tcx().sess.span_note(
-                    sp,
-                    fmt!("impure due to %s", msg));
+        fn check_for_assignment_to_restricted_or_frozen_location(
+            self: &CheckLoanCtxt,
+            expr: @ast::expr,
+            cmt: mc::cmt) -> bool
+        {
+            //! Check for assignments that violate the terms of an
+            //! outstanding loan.
+
+            let loan_path = match opt_loan_path(cmt) {
+                Some(lp) => lp,
+                None => { return true; /* no loan path, can't be any loans */ }
+            };
+
+            // Start by searching for an assignment to a *restricted*
+            // location. Here is one example of the kind of error caught
+            // by this check:
+            //
+            //    let mut v = ~[1, 2, 3];
+            //    let p = &v;
+            //    v = ~[4];
+            //
+            // In this case, creating `p` triggers a RESTR_MUTATE
+            // restriction on the path `v`.
+            //
+            // Here is a second, more subtle example:
+            //
+            //    let mut v = ~[1, 2, 3];
+            //    let p = &const v[0];
+            //    v[0] = 4;                   // OK
+            //    v[1] = 5;                   // OK
+            //    v = ~[4, 5, 3];             // Error
+            //
+            // In this case, `p` is pointing to `v[0]`, and it is a
+            // `const` pointer in any case. So the first two
+            // assignments are legal (and would be permitted by this
+            // check). However, the final assignment (which is
+            // logically equivalent) is forbidden, because it would
+            // cause the existing `v` array to be freed, thus
+            // invalidating `p`. In the code, this error results
+            // because `gather_loans::restrictions` adds a
+            // `RESTR_MUTATE` restriction whenever the contents of an
+            // owned pointer are borrowed, and hence while `v[*]` is not
+            // restricted from being written, `v` is.
+            for self.each_in_scope_restriction(expr.id, loan_path)
+                |loan, restr|
+            {
+                if restr.set.intersects(RESTR_MUTATE) {
+                    self.report_illegal_mutation(expr, loan_path, loan);
+                    return false;
+                }
+            }
+
+            // The previous code handled assignments to paths that
+            // have been restricted. This covers paths that have been
+            // directly lent out and their base paths, but does not
+            // cover random extensions of those paths. For example,
+            // the following program is not declared illegal by the
+            // previous check:
+            //
+            //    let mut v = ~[1, 2, 3];
+            //    let p = &v;
+            //    v[0] = 4; // declared error by loop below, not code above
+            //
+            // The reason that this passes the previous check whereas
+            // an assignment like `v = ~[4]` fails is because the assignment
+            // here is to `v[*]`, and the existing restrictions were issued
+            // for `v`, not `v[*]`.
+            //
+            // So in this loop, we walk back up the loan path so long
+            // as the mutability of the path is dependent on a super
+            // path, and check that the super path was not lent out as
+            // mutable or immutable (a const loan is ok).
+            //
+            // Note that we are *not* checking for any and all
+            // restrictions.  We are only interested in the pointers
+            // that the user created, whereas we add restrictions for
+            // all kinds of paths that are not directly aliased. If we checked
+            // for all restrictions, and not just loans, then the following
+            // valid program would be considered illegal:
+            //
+            //    let mut v = ~[1, 2, 3];
+            //    let p = &const v[0];
+            //    v[1] = 5; // ok
+            //
+            // Here the restriction that `v` not be mutated would be misapplied
+            // to block the subpath `v[1]`.
+            let full_loan_path = loan_path;
+            let mut loan_path = loan_path;
+            loop {
+                match *loan_path {
+                    // Peel back one layer if `loan_path` has
+                    // inherited mutability
+                    LpExtend(lp_base, mc::McInherited, _) => {
+                        loan_path = lp_base;
+                    }
+
+                    // Otherwise stop iterating
+                    LpExtend(_, mc::McDeclared, _) |
+                    LpExtend(_, mc::McImmutable, _) |
+                    LpExtend(_, mc::McReadOnly, _) |
+                    LpVar(_) => {
+                        return true;
+                    }
+                }
+
+                // Check for a non-const loan of `loan_path`
+                for self.each_in_scope_loan(expr.id) |loan| {
+                    if loan.loan_path == loan_path && loan.mutbl != m_const {
+                        self.report_illegal_mutation(expr, full_loan_path, loan);
+                        return false;
+                    }
+                }
             }
-          }
         }
     }
 
-    fn check_move_out_from_expr(@mut self, ex: @ast::expr) {
+    fn report_illegal_mutation(&self,
+                               expr: @ast::expr,
+                               loan_path: &LoanPath,
+                               loan: &Loan) {
+        self.bccx.span_err(
+            expr.span,
+            fmt!("cannot assign to `%s` because it is borrowed",
+                 self.bccx.loan_path_to_str(loan_path)));
+        self.bccx.span_note(
+            loan.span,
+            fmt!("borrow of `%s` occurs here",
+                 self.bccx.loan_path_to_str(loan_path)));
+    }
+
+    fn check_move_out_from_expr(&self, ex: @ast::expr) {
         match ex.node {
             ast::expr_paren(*) => {
                 /* In the case of an expr_paren(), the expression inside
@@ -529,52 +527,57 @@ pub impl CheckLoanCtxt {
                     MoveFromIllegalCmt(_) => {
                         self.bccx.span_err(
                             cmt.span,
-                            fmt!("moving out of %s",
+                            fmt!("cannot move out of %s",
                                  self.bccx.cmt_to_str(cmt)));
                     }
-                    MoveWhileBorrowed(_, loan_cmt) => {
+                    MoveWhileBorrowed(loan_path, loan_span) => {
                         self.bccx.span_err(
                             cmt.span,
-                            fmt!("moving out of %s prohibited \
-                                  due to outstanding loan",
-                                 self.bccx.cmt_to_str(cmt)));
+                            fmt!("cannot move out of `%s` \
+                                  because it is borrowed",
+                                 self.bccx.loan_path_to_str(loan_path)));
                         self.bccx.span_note(
-                            loan_cmt.span,
-                            fmt!("loan of %s granted here",
-                                 self.bccx.cmt_to_str(loan_cmt)));
+                            loan_span,
+                            fmt!("borrow of `%s` occurs here",
+                                 self.bccx.loan_path_to_str(loan_path)));
                     }
                 }
             }
         }
     }
 
-    fn analyze_move_out_from_cmt(&mut self, cmt: cmt) -> MoveError {
-        debug!("check_move_out_from_cmt(cmt=%s)",
-               self.bccx.cmt_to_repr(cmt));
+    fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError {
+        debug!("check_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
 
         match cmt.cat {
-          // Rvalues, locals, and arguments can be moved:
-          cat_rvalue | cat_local(_) | cat_arg(_) | cat_self(_) => {}
-
-          // We allow moving out of static items because the old code
-          // did.  This seems consistent with permitting moves out of
-          // rvalues, I guess.
-          cat_special(sk_static_item) => {}
-
-          cat_deref(_, _, unsafe_ptr) => {}
-
-          // Nothing else.
-          _ => {
-              return MoveFromIllegalCmt(cmt);
-          }
+            // Rvalues, locals, and arguments can be moved:
+            mc::cat_rvalue | mc::cat_local(_) |
+            mc::cat_arg(_, ast::by_copy) | mc::cat_self(_) => {}
+
+            // It seems strange to allow a move out of a static item,
+            // but what happens in practice is that you have a
+            // reference to a constant with a type that should be
+            // moved, like `None::<~int>`.  The type of this constant
+            // is technically `Option<~int>`, which moves, but we know
+            // that the content of static items will never actually
+            // contain allocated pointers, so we can just memcpy it.
+            mc::cat_static_item => {}
+
+            mc::cat_deref(_, _, mc::unsafe_ptr(*)) => {}
+
+            // Nothing else.
+            _ => {
+                return MoveFromIllegalCmt(cmt);
+            }
         }
 
-        self.bccx.add_to_mutbl_map(cmt);
+        // NOTE inadequare if/when we permit `move a.b`
 
         // check for a conflicting loan:
-        for cmt.lp.each |lp| {
-            for self.walk_loans_of(cmt.id, *lp) |loan| {
-                return MoveWhileBorrowed(cmt, loan.cmt);
+        for opt_loan_path(cmt).each |&lp| {
+            for self.each_in_scope_restriction(cmt.id, lp) |loan, _| {
+                // Any restriction prevents moves.
+                return MoveWhileBorrowed(loan.loan_path, loan.span);
             }
         }
 
@@ -582,105 +585,46 @@ pub impl CheckLoanCtxt {
     }
 
     fn check_call(&mut self,
-                  expr: @ast::expr,
-                  callee: Option<@ast::expr>,
-                  callee_id: ast::node_id,
-                  callee_span: span,
-                  args: &[@ast::expr]) {
-        let pc = self.purity(expr.id);
-        match pc {
-            // no purity, no need to check for anything
-            Right(pc_default) => return,
-
-            // some form of purity, definitely need to check
-            Left(_) => (),
-
-            // Unsafe trumped. To see if the unsafe is necessary, see what the
-            // purity would have been without a trump, and if it's some form
-            // of purity then we need to go ahead with the check
-            Right(pc_unsafe) => {
-                match do with(&mut self.declared_purity.purity,
-                              ast::impure_fn) { self.purity(expr.id) } {
-                    Right(pc_unsafe) => fail!(~"unsafe can't trump twice"),
-                    Right(pc_default) => return,
-                    Left(_) => ()
-                }
-            }
-
-        }
-        self.check_pure_callee_or_arg(
-            pc, callee, callee_id, callee_span);
-        for args.each |arg| {
-            self.check_pure_callee_or_arg(
-                pc, Some(*arg), arg.id, arg.span);
-        }
+                  _expr: @ast::expr,
+                  _callee: Option<@ast::expr>,
+                  _callee_id: ast::node_id,
+                  _callee_span: span,
+                  _args: &[@ast::expr])
+    {
+        // NB: This call to check for conflicting loans is not truly
+        // necessary, because the callee_id never issues new loans.
+        // However, I added it for consistency and lest the system
+        // should change in the future.
+        //
+        // FIXME(#5074) nested method calls
+        // self.check_for_conflicting_loans(callee_id);
     }
 }
 
-fn check_loans_in_fn(fk: &visit::fn_kind,
-                     decl: &ast::fn_decl,
-                     body: &ast::blk,
-                     sp: span,
-                     id: ast::node_id,
-                     self: @mut CheckLoanCtxt,
-                     visitor: visit::vt<@mut CheckLoanCtxt>) {
-    let is_stack_closure = self.is_stack_closure(id);
-    let fty = ty::node_id_to_type(self.tcx(), id);
-
-    let declared_purity, src;
+fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
+                         decl: &ast::fn_decl,
+                         body: &ast::blk,
+                         sp: span,
+                         id: ast::node_id,
+                         self: @mut CheckLoanCtxt<'a>,
+                         visitor: visit::vt<@mut CheckLoanCtxt<'a>>) {
     match *fk {
-        visit::fk_item_fn(*) | visit::fk_method(*) |
+        visit::fk_item_fn(*) |
+        visit::fk_method(*) |
         visit::fk_dtor(*) => {
-            declared_purity = ty::ty_fn_purity(fty);
-            src = id;
+            // Don't process nested items.
+            return;
         }
 
-        visit::fk_anon(*) | visit::fk_fn_block(*) => {
+        visit::fk_anon(*) |
+        visit::fk_fn_block(*) => {
+            let fty = ty::node_id_to_type(self.tcx(), id);
             let fty_sigil = ty::ty_closure_sigil(fty);
             check_moves_from_captured_variables(self, id, fty_sigil);
-            let pair = ty::determine_inherited_purity(
-                (self.declared_purity.purity, self.declared_purity.def),
-                (ty::ty_fn_purity(fty), id),
-                fty_sigil);
-            declared_purity = pair.first();
-            src = pair.second();
         }
     }
 
-    debug!("purity on entry=%?", copy self.declared_purity);
-    do save_and_restore_managed(self.declared_purity) {
-        do save_and_restore_managed(self.fn_args) {
-            self.declared_purity = @mut PurityState::function(declared_purity, src);
-
-            match *fk {
-                visit::fk_anon(*) |
-                visit::fk_fn_block(*) if is_stack_closure => {
-                    // inherits the fn_args from enclosing ctxt
-                }
-                visit::fk_anon(*) | visit::fk_fn_block(*) |
-                visit::fk_method(*) | visit::fk_item_fn(*) |
-                visit::fk_dtor(*) => {
-                    let mut fn_args = ~[];
-                    for decl.inputs.each |input| {
-                        // For the purposes of purity, only consider function-
-                        // typed bindings in trivial patterns to be function
-                        // arguments. For example, do not allow `f` and `g` in
-                        // (f, g): (&fn(), &fn()) to be called.
-                        match input.pat.node {
-                            ast::pat_ident(_, _, None) => {
-                                fn_args.push(input.pat.id);
-                            }
-                            _ => {} // Ignore this argument.
-                        }
-                    }
-                    *self.fn_args = @fn_args;
-                }
-            }
-
-            visit::visit_fn(fk, decl, body, sp, id, self, visitor);
-        }
-    }
-    debug!("purity on exit=%?", copy self.declared_purity);
+    visit::visit_fn(fk, decl, body, sp, id, self, visitor);
 
     fn check_moves_from_captured_variables(self: @mut CheckLoanCtxt,
                                            id: ast::node_id,
@@ -706,16 +650,16 @@ fn check_loans_in_fn(fk: &visit::fn_kind,
                                 fmt!("illegal by-move capture of %s",
                                      self.bccx.cmt_to_str(move_cmt)));
                         }
-                        MoveWhileBorrowed(move_cmt, loan_cmt) => {
+                        MoveWhileBorrowed(loan_path, loan_span) => {
                             self.bccx.span_err(
                                 cap_var.span,
-                                fmt!("by-move capture of %s prohibited \
-                                      due to outstanding loan",
-                                     self.bccx.cmt_to_str(move_cmt)));
+                                fmt!("cannot move `%s` into closure \
+                                      because it is borrowed",
+                                     self.bccx.loan_path_to_str(loan_path)));
                             self.bccx.span_note(
-                                loan_cmt.span,
-                                fmt!("loan of %s granted here",
-                                     self.bccx.cmt_to_str(loan_cmt)));
+                                loan_span,
+                                fmt!("borrow of `%s` occurs here",
+                                     self.bccx.loan_path_to_str(loan_path)));
                         }
                     }
                 }
@@ -726,17 +670,19 @@ fn check_loans_in_fn(fk: &visit::fn_kind,
     }
 }
 
-fn check_loans_in_local(local: @ast::local,
-                        self: @mut CheckLoanCtxt,
-                        vt: visit::vt<@mut CheckLoanCtxt>) {
+fn check_loans_in_local<'a>(local: @ast::local,
+                            self: @mut CheckLoanCtxt<'a>,
+                            vt: visit::vt<@mut CheckLoanCtxt<'a>>) {
     visit::visit_local(local, self, vt);
 }
 
-fn check_loans_in_expr(expr: @ast::expr,
-                       self: @mut CheckLoanCtxt,
-                       vt: visit::vt<@mut CheckLoanCtxt>) {
-    debug!("check_loans_in_expr(expr=%?/%s)",
-           expr.id, pprust::expr_to_str(expr, self.tcx().sess.intr()));
+fn check_loans_in_expr<'a>(expr: @ast::expr,
+                           self: @mut CheckLoanCtxt<'a>,
+                           vt: visit::vt<@mut CheckLoanCtxt<'a>>) {
+    debug!("check_loans_in_expr(expr=%s)",
+           expr.repr(self.tcx()));
+
+    visit::visit_expr(expr, self, vt);
 
     self.check_for_conflicting_loans(expr.id);
 
@@ -746,12 +692,12 @@ fn check_loans_in_expr(expr: @ast::expr,
 
     match expr.node {
       ast::expr_swap(l, r) => {
-        self.check_assignment(at_swap, l);
-        self.check_assignment(at_swap, r);
+        self.check_assignment(l);
+        self.check_assignment(r);
       }
       ast::expr_assign(dest, _) |
       ast::expr_assign_op(_, dest, _) => {
-        self.check_assignment(at_straight_up, dest);
+        self.check_assignment(dest);
       }
       ast::expr_call(f, ref args, _) => {
         self.check_call(expr, Some(f), f.id, f.span, *args);
@@ -776,32 +722,35 @@ fn check_loans_in_expr(expr: @ast::expr,
                         expr.span,
                         ~[]);
       }
-      ast::expr_match(*) => {
-          // Note: moves out of pattern bindings are not checked by
-          // the borrow checker, at least not directly.  What happens
-          // is that if there are any moved bindings, the discriminant
-          // will be considered a move, and this will be checked as
-          // normal.  Then, in `middle::check_match`, we will check
-          // that no move occurs in a binding that is underneath an
-          // `@` or `&`.  Together these give the same guarantees as
-          // `check_move_out_from_expr()` without requiring us to
-          // rewalk the patterns and rebuild the pattern
-          // categorizations.
-      }
       _ => { }
     }
-
-    visit::visit_expr(expr, self, vt);
 }
 
-fn check_loans_in_block(blk: &ast::blk,
-                        self: @mut CheckLoanCtxt,
-                        vt: visit::vt<@mut CheckLoanCtxt>) {
-    do save_and_restore_managed(self.declared_purity) {
-        self.check_for_conflicting_loans(blk.node.id);
+fn check_loans_in_pat<'a>(pat: @ast::pat,
+                          self: @mut CheckLoanCtxt<'a>,
+                          vt: visit::vt<@mut CheckLoanCtxt<'a>>)
+{
+    self.check_for_conflicting_loans(pat.id);
+
+    // Note: moves out of pattern bindings are not checked by
+    // the borrow checker, at least not directly.  What happens
+    // is that if there are any moved bindings, the discriminant
+    // will be considered a move, and this will be checked as
+    // normal.  Then, in `middle::check_match`, we will check
+    // that no move occurs in a binding that is underneath an
+    // `@` or `&`.  Together these give the same guarantees as
+    // `check_move_out_from_expr()` without requiring us to
+    // rewalk the patterns and rebuild the pattern
+    // categorizations.
+
+    visit::visit_pat(pat, self, vt);
+}
 
-        *self.declared_purity = self.declared_purity.recurse(blk);
-        visit::visit_block(blk, self, vt);
-    }
+fn check_loans_in_block<'a>(blk: &ast::blk,
+                            self: @mut CheckLoanCtxt<'a>,
+                            vt: visit::vt<@mut CheckLoanCtxt<'a>>)
+{
+    visit::visit_block(blk, self, vt);
+    self.check_for_conflicting_loans(blk.node.id);
 }
 
diff --git a/src/librustc/middle/borrowck/doc.rs b/src/librustc/middle/borrowck/doc.rs
new file mode 100644
index 00000000000..1e09fbe7184
--- /dev/null
+++ b/src/librustc/middle/borrowck/doc.rs
@@ -0,0 +1,750 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+
+# The Borrow Checker
+
+This pass has the job of enforcing memory safety. This is a subtle
+topic. The only way I know how to explain it is terms of a formal
+model, so that's what I'll do.
+
+# Formal model
+
+Let's consider a simple subset of Rust in which you can only borrow
+from lvalues like so:
+
+    LV = x | LV.f | *LV
+
+Here `x` represents some variable, `LV.f` is a field reference,
+and `*LV` is a pointer dereference. There is no auto-deref or other
+niceties. This means that if you have a type like:
+
+    struct S { f: uint }
+
+and a variable `a: ~S`, then the rust expression `a.f` would correspond
+to an `LV` of `(*a).f`.
+
+Here is the formal grammar for the types we'll consider:
+
+    TY = () | S<'LT...> | ~TY | & 'LT MQ TY | @ MQ TY
+    MQ = mut | imm | const
+
+Most of these types should be pretty self explanatory. Here `S` is a
+struct name and we assume structs are declared like so:
+
+    SD = struct S<'LT...> { (f: TY)... }
+
+# An intuitive explanation
+
+## Issuing loans
+
+Now, imagine we had a program like this:
+
+    struct Foo { f: uint, g: uint }
+    ...
+    'a: {
+      let mut x: ~Foo = ...;
+      let y = &mut (*x).f;
+      x = ...;
+    }
+
+This is of course dangerous because mutating `x` will free the old
+value and hence invalidate `y`. The borrow checker aims to prevent
+this sort of thing.
+
+### Loans
+
+The way the borrow checker works is that it analyzes each borrow
+expression (in our simple model, that's stuff like `&LV`, though in
+real life there are a few other cases to consider). For each borrow
+expression, it computes a vector of loans:
+
+    LOAN = (LV, LT, PT, LK)
+    PT = Partial | Total
+    LK = MQ | RESERVE
+
+Each `LOAN` tuple indicates some sort of restriction on what can be
+done to the lvalue `LV`; `LV` will always be a path owned by the
+current stack frame. These restrictions are called "loans" because
+they are always the result of a borrow expression.
+
+Every loan has a lifetime `LT` during which those restrictions are in
+effect.  The indicator `PT` distinguishes between *total* loans, in
+which the LV itself was borrowed, and *partial* loans, which means
+that some content ownwed by LV was borrowed.
+
+The final element in the loan tuple is the *loan kind* `LK`.  There
+are four kinds: mutable, immutable, const, and reserve:
+
+- A "mutable" loan means that LV may be written to through an alias, and
+  thus LV cannot be written to directly or immutably aliased (remember
+  that we preserve the invariant that any given value can only be
+  written to through one path at a time; hence if there is a mutable
+  alias to LV, then LV cannot be written directly until this alias is
+  out of scope).
+
+- An "immutable" loan means that LV must remain immutable.  Hence it
+  cannot be written, but other immutable aliases are permitted.
+
+- A "const" loan means that an alias to LV exists.  LV may still be
+  written or frozen.
+
+- A "reserve" loan is the strongest case.  It prevents both mutation
+  and aliasing of any kind, including `&const` loans.  Reserve loans
+  are a side-effect of borrowing an `&mut` loan.
+
+In addition to affecting mutability, a loan of any kind implies that
+LV cannot be moved.
+
+### Example
+
+To give you a better feeling for what a loan is, let's look at three
+loans that would be issued as a result of the borrow `&(*x).f` in the
+example above:
+
+    ((*x).f, Total, mut, 'a)
+    (*x, Partial, mut, 'a)
+    (x, Partial, mut, 'a)
+
+The first loan states that the expression `(*x).f` has been loaned
+totally as mutable for the lifetime `'a`. This first loan would
+prevent an assignment `(*x).f = ...` from occurring during the
+lifetime `'a`.
+
+Now let's look at the second loan. You may have expected that each
+borrow would result in only one loan. But this is not the case.
+Instead, there will be loans for every path where mutation might
+affect the validity of the borrowed pointer that is created (in some
+cases, there can even be multiple loans per path, see the section on
+"Borrowing in Calls" below for the gory details). The reason for this
+is to prevent actions that would indirectly affect the borrowed path.
+In this case, we wish to ensure that `(*x).f` is not mutated except
+through the mutable alias `y`.  Therefore, we must not only prevent an
+assignment to `(*x).f` but also an assignment like `*x = Foo {...}`,
+as this would also mutate the field `f`.  To do so, we issue a
+*partial* mutable loan for `*x` (the loan is partial because `*x`
+itself was not borrowed).  This partial loan will cause any attempt to
+assign to `*x` to be flagged as an error.
+
+Because both partial and total loans prevent assignments, you may
+wonder why we bother to distinguish between them.  The reason for this
+distinction has to do with preventing double borrows. In particular,
+it is legal to borrow both `&mut x.f` and `&mut x.g` simultaneously,
+but it is not legal to borrow `&mut x.f` twice. In the borrow checker,
+the first case would result in two *partial* mutable loans of `x`
+(along with one total mutable loan of `x.f` and one of `x.g) whereas
+the second would result in two *total* mutable loans of `x.f` (along
+with two partial mutable loans of `x`).  Multiple *total mutable* loan
+for the same path are not permitted, but multiple *partial* loans (of
+any mutability) are permitted.
+
+Finally, we come to the third loan. This loan is a partial mutable
+loan of `x`.  This loan prevents us from reassigning `x`, which would
+be bad for two reasons.  First, it would change the value of `(*x).f`
+but, even worse, it would cause the pointer `y` to become a dangling
+pointer.  Bad all around.
+
+## Checking for illegal assignments, moves, and reborrows
+
+Once we have computed the loans introduced by each borrow, the borrow
+checker will determine the full set of loans in scope at each
+expression and use that to decide whether that expression is legal.
+Remember that the scope of loan is defined by its lifetime LT.  We
+sometimes say that a loan which is in-scope at a particular point is
+an "outstanding loan".
+
+The kinds of expressions which in-scope loans can render illegal are
+*assignments*, *moves*, and *borrows*.
+
+An assignments to an lvalue LV is illegal if there is in-scope mutable
+or immutable loan for LV.  Assignment with an outstanding mutable loan
+is illegal because then the `&mut` pointer is supposed to be the only
+way to mutate the value.  Assignment with an outstanding immutable
+loan is illegal because the value is supposed to be immutable at that
+point.
+
+A move from an lvalue LV is illegal if there is any sort of
+outstanding loan.
+
+A borrow expression may be illegal if any of the loans which it
+produces conflict with other outstanding loans.  Two loans are
+considered compatible if one of the following conditions holds:
+
+- At least one loan is a const loan.
+- Both loans are partial loans.
+- Both loans are immutable.
+
+Any other combination of loans is illegal.
+
+# The set of loans that results from a borrow expression
+
+Here we'll define four functions---MUTATE, FREEZE, ALIAS, and
+TAKE---which are all used to compute the set of LOANs that result
+from a borrow expression.  The first three functions each have
+a similar type signature:
+
+    MUTATE(LV, LT, PT) -> LOANS
+    FREEZE(LV, LT, PT) -> LOANS
+    ALIAS(LV, LT, PT) -> LOANS
+
+MUTATE, FREEZE, and ALIAS are used when computing the loans result
+from mutable, immutable, and const loans respectively.  For example,
+the loans resulting from an expression like `&mut (*x).f` would be
+computed by `MUTATE((*x).f, LT, Total)`, where `LT` is the lifetime of
+the resulting pointer.  Similarly the loans for `&(*x).f` and `&const
+(*x).f` would be computed by `FREEZE((*x).f, LT, Total)` and
+`ALIAS((*x).f, LT, Total)` respectively. (Actually this is a slight
+simplification; see the section below on Borrows in Calls for the full
+gory details)
+
+The names MUTATE, FREEZE, and ALIAS are intended to suggest the
+semantics of `&mut`, `&`, and `&const` borrows respectively.  `&mut`,
+for example, creates a mutable alias of LV.  `&` causes the borrowed
+value to be frozen (immutable).  `&const` does neither but does
+introduce an alias to be the borrowed value.
+
+Each of these three functions is only defined for some inputs.  That
+is, it may occur that some particular borrow is not legal.  For
+example, it is illegal to make an `&mut` loan of immutable data.  In
+that case, the MUTATE() function is simply not defined (in the code,
+it returns a Result<> condition to indicate when a loan would be
+illegal).
+
+The final function, RESERVE, is used as part of borrowing an `&mut`
+pointer.  Due to the fact that it is used for one very particular
+purpose, it has a rather simpler signature than the others:
+
+    RESERVE(LV, LT) -> LOANS
+
+It is explained when we come to that case.
+
+## The function MUTATE()
+
+Here we use [inference rules][ir] to define the MUTATE() function.
+We will go case by case for the various kinds of lvalues that
+can be borrowed.
+
+[ir]: http://en.wikipedia.org/wiki/Rule_of_inference
+
+### Mutating local variables
+
+The rule for mutating local variables is as follows:
+
+    Mutate-Variable:
+      LT <= Scope(x)
+      Mut(x) = Mut
+      --------------------------------------------------
+      MUTATE(x, LT, PT) = (x, LT, PT, mut)
+
+Here `Scope(x)` is the lifetime of the block in which `x` was declared
+and `Mut(x)` indicates the mutability with which `x` was declared.
+This rule simply states that you can only create a mutable alias
+to a variable if it is mutable, and that alias cannot outlive the
+stack frame in which the variable is declared.
+
+### Mutating fields and owned pointers
+
+As it turns out, the rules for mutating fields and mutating owned
+pointers turn out to be quite similar.  The reason is that the
+expressions `LV.f` and `*LV` are both owned by their base expression
+`LV`.  So basically the result of mutating `LV.f` or `*LV` is computed
+by adding a loan for `LV.f` or `*LV` and then the loans for a partial
+take of `LV`:
+
+    Mutate-Field:
+      MUTATE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      MUTATE(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, mut)
+
+    Mutate-Owned-Ptr:
+      Type(LV) = ~Ty
+      MUTATE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      MUTATE(*LV, LT, PT) = LOANS, (*LV, LT, PT, mut)
+
+Note that while our micro-language only has fields, the slight
+variations on the `Mutate-Field` rule are used for any interior content
+that appears in the full Rust language, such as the contents of a
+tuple, fields in a struct, or elements of a fixed-length vector.
+
+### Mutating dereferenced borrowed pointers
+
+The rule for borrowed pointers is by far the most complicated:
+
+    Mutate-Mut-Borrowed-Ptr:
+      Type(LV) = &LT_P mut Ty             // (1)
+      LT <= LT_P                          // (2)
+      RESERVE(LV, LT) = LOANS             // (3)
+      ------------------------------------------------------------
+      MUTATE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Mut)
+
+Condition (1) states that only a mutable borrowed pointer can be
+taken.  Condition (2) states that the lifetime of the alias must be
+less than the lifetime of the borrowed pointer being taken.
+
+Conditions (3) and (4) are where things get interesting.  The intended
+semantics of the borrow is that the new `&mut` pointer is the only one
+which has the right to modify the data; the original `&mut` pointer
+must not be used for mutation.  Because borrowed pointers do not own
+their content nor inherit mutability, we must be particularly cautious
+of aliases, which could permit the original borrowed pointer to be
+reached from another path and thus circumvent our loans.
+
+Here is one example of what could go wrong if we ignore clause (4):
+
+    let x: &mut T;
+    ...
+    let y = &mut *x;   // Only *y should be able to mutate...
+    let z = &const x;
+    **z = ...;         // ...but here **z is still able to mutate!
+
+Another possible error could occur with moves:
+
+    let x: &mut T;
+    ...
+    let y = &mut *x;   // Issues loan: (*x, LT, Total, Mut)
+    let z = x;         // moves from x
+    *z = ...;          // Mutates *y indirectly! Bad.
+
+In both of these cases, the problem is that when creating the alias
+`y` we would only issue a loan preventing assignment through `*x`.
+But this loan can be easily circumvented by moving from `x` or
+aliasing it.  Note that, in the first example, the alias of `x` was
+created using `&const`, which is a particularly weak form of alias.
+
+The danger of aliases can also occur when the `&mut` pointer itself
+is already located in an alias location, as here:
+
+    let x: @mut &mut T; // or &mut &mut T, &&mut T,
+    ...                 // &const &mut T, @&mut T, etc
+    let y = &mut **x;   // Only *y should be able to mutate...
+    let z = x;
+    **z = ...;          // ...but here **z is still able to mutate!
+
+When we cover the rules for RESERVE, we will see that it would
+disallow this case, because MUTATE can only be applied to canonical
+lvalues which are owned by the current stack frame.
+
+It might be the case that if `&const` and `@const` pointers were
+removed, we could do away with RESERVE and simply use MUTATE instead.
+But we have to be careful about the final example in particular, since
+dynamic freezing would not be sufficient to prevent this example.
+Perhaps a combination of MUTATE with a predicate OWNED(LV).
+
+One final detail: unlike every other case, when we calculate the loans
+using RESERVE we do not use the original lifetime `LT` but rather
+`GLB(Scope(LV), LT)`.  What this says is:
+
+### Mutating dereferenced managed pointers
+
+Because the correctness of managed pointer loans is checked dynamically,
+the rule is quite simple:
+
+    Mutate-Mut-Managed-Ptr:
+      Type(LV) = @mut Ty
+      Add ROOT-FREEZE annotation for *LV with lifetime LT
+      ------------------------------------------------------------
+      MUTATE(*LV, LT, Total) = []
+
+No loans are issued.  Instead, we add a side annotation that causes
+`*LV` to be rooted and frozen on entry to LV.  You could rephrase
+these rules as having multiple returns values, or rephrase this as a
+kind of loan, but whatever.
+
+One interesting point is that *partial takes* of `@mut` are forbidden.
+This is not for any soundness reason but just because it is clearer
+for users when `@mut` values are either lent completely or not at all.
+
+## The function FREEZE
+
+The rules for FREEZE are pretty similar to MUTATE.  The first four
+cases I'll just present without discussion, as the reasoning is
+quite analogous to the MUTATE case:
+
+    Freeze-Variable:
+      LT <= Scope(x)
+      --------------------------------------------------
+      FREEZE(x, LT, PT) = (x, LT, PT, imm)
+
+    Freeze-Field:
+      FREEZE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      FREEZE(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, imm)
+
+    Freeze-Owned-Ptr:
+      Type(LV) = ~Ty
+      FREEZE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, imm)
+
+    Freeze-Mut-Borrowed-Ptr:
+      Type(LV) = &LT_P mut Ty
+      LT <= LT_P
+      RESERVE(LV, LT) = LOANS
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Imm)
+
+    Freeze-Mut-Managed-Ptr:
+      Type(LV) = @mut Ty
+      Add ROOT-FREEZE annotation for *LV with lifetime LT
+      ------------------------------------------------------------
+      Freeze(*LV, LT, Total) = []
+
+The rule to "freeze" an immutable borrowed pointer is quite
+simple, since the content is already immutable:
+
+    Freeze-Imm-Borrowed-Ptr:
+      Type(LV) = &LT_P Ty                 // (1)
+      LT <= LT_P                          // (2)
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Mut)
+
+The final two rules pertain to borrows of `@Ty`.  There is a bit of
+subtlety here.  The main problem is that we must guarantee that the
+managed box remains live for the entire borrow.  We can either do this
+dynamically, by rooting it, or (better) statically, and hence there
+are two rules:
+
+    Freeze-Imm-Managed-Ptr-1:
+      Type(LV) = @Ty
+      Add ROOT annotation for *LV
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = []
+
+    Freeze-Imm-Managed-Ptr-2:
+      Type(LV) = @Ty
+      LT <= Scope(LV)
+      Mut(LV) = imm
+      LV is not moved
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = []
+
+The intention of the second rule is to avoid an extra root if LV
+serves as a root.  In that case, LV must (1) outlive the borrow; (2)
+be immutable; and (3) not be moved.
+
+## The ALIAS function
+
+The function ALIAS is used for `&const` loans but also to handle one
+corner case concerning function arguments (covered in the section
+"Borrows in Calls" below).  It computes the loans that result from
+observing that there is a pointer to `LV` and thus that pointer must
+remain valid.
+
+The first two rules are simple:
+
+    Alias-Variable:
+      LT <= Scope(x)
+      --------------------------------------------------
+      ALIAS(x, LT, PT) = (x, LT, PT, Const)
+
+    Alias-Field:
+      ALIAS(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      ALIAS(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, Const)
+
+### Aliasing owned pointers
+
+The rule for owned pointers is somewhat interesting:
+
+    Alias-Owned-Ptr:
+      Type(LV) = ~Ty
+      FREEZE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      ALIAS(*LV, LT, PT) = LOANS, (*LV, LT, PT, Const)
+
+Here we *freeze* the base `LV`.  The reason is that if an owned
+pointer is mutated it frees its content, which means that the alias to
+`*LV` would become a dangling pointer.
+
+### Aliasing borrowed pointers
+
+The rule for borrowed pointers is quite simple, because borrowed
+pointers do not own their content and thus do not play a role in
+keeping it live:
+
+    Alias-Borrowed-Ptr:
+      Type(LV) = &LT_P MQ Ty
+      LT <= LT_P
+      ------------------------------------------------------------
+      ALIAS(*LV, LT, PT) = []
+
+Basically, the existence of a borrowed pointer to some memory with
+lifetime LT_P is proof that the memory can safely be aliased for any
+lifetime LT <= LT_P.
+
+### Aliasing managed pointers
+
+The rules for aliasing managed pointers are similar to those
+used with FREEZE, except that they apply to all manager pointers
+regardles of mutability:
+
+    Alias-Managed-Ptr-1:
+      Type(LV) = @MQ Ty
+      Add ROOT annotation for *LV
+      ------------------------------------------------------------
+      ALIAS(*LV, LT, PT) = []
+
+    Alias-Managed-Ptr-2:
+      Type(LV) = @MQ Ty
+      LT <= Scope(LV)
+      Mut(LV) = imm
+      LV is not moved
+      ------------------------------------------------------------
+      ALIAS(*LV, LT, PT) = []
+
+## The RESERVE function
+
+The final function, RESERVE, is used for loans of `&mut` pointers.  As
+discussed in the section on the function MUTATE, we must be quite
+careful when "re-borrowing" an `&mut` pointer to ensure that the original
+`&mut` pointer can no longer be used to mutate.
+
+There are a couple of dangers to be aware of:
+
+- `&mut` pointers do not inherit mutability.  Therefore, if you have
+  an lvalue LV with type `&mut T` and you freeze `LV`, you do *not*
+  freeze `*LV`.  This is quite different from an `LV` with type `~T`.
+
+- Also, because they do not inherit mutability, if the `&mut` pointer
+  lives in an aliased location, then *any alias* can be used to write!
+
+As a consequence of these two rules, RESERVE can only be successfully
+invoked on an lvalue LV that is *owned by the current stack frame*.
+This ensures that there are no aliases that are not visible from the
+outside.  Moreover, Reserve loans are incompatible with all other
+loans, even Const loans.  This prevents any aliases from being created
+within the current function.
+
+### Reserving local variables
+
+The rule for reserving a variable is generally straightforward but
+with one interesting twist:
+
+    Reserve-Variable:
+      --------------------------------------------------
+      RESERVE(x, LT) = (x, LT, Total, Reserve)
+
+The twist here is that the incoming lifetime is not required to
+be a subset of the incoming variable, unlike every other case.  To
+see the reason for this, imagine the following function:
+
+    struct Foo { count: uint }
+    fn count_field(x: &'a mut Foo) -> &'a mut count {
+        &mut (*x).count
+    }
+
+This function consumes one `&mut` pointer and returns another with the
+same lifetime pointing at a particular field.  The borrow for the
+`&mut` expression will result in a call to `RESERVE(x, 'a)`, which is
+intended to guarantee that `*x` is not later aliased or used to
+mutate.  But the lifetime of `x` is limited to the current function,
+which is a sublifetime of the parameter `'a`, so the rules used for
+MUTATE, FREEZE, and ALIAS (which require that the lifetime of the loan
+not exceed the lifetime of the variable) would result in an error.
+
+Nonetheless this function is perfectly legitimate.  After all, the
+caller has moved in an `&mut` pointer with lifetime `'a`, and thus has
+given up their right to mutate the value for the remainder of `'a`.
+So it is fine for us to return a pointer with the same lifetime.
+
+The reason that RESERVE differs from the other functions is that
+RESERVE is not responsible for guaranteeing that the pointed-to data
+will outlive the borrowed pointer being created.  After all, `&mut`
+values do not own the data they point at.
+
+### Reserving owned content
+
+The rules for fields and owned pointers are very straightforward:
+
+    Reserve-Field:
+      RESERVE(LV, LT) = LOANS
+      ------------------------------------------------------------
+      RESERVE(LV.f, LT) = LOANS, (LV.F, LT, Total, Reserve)
+
+    Reserve-Owned-Ptr:
+      Type(LV) = ~Ty
+      RESERVE(LV, LT) = LOANS
+      ------------------------------------------------------------
+      RESERVE(*LV, LT) = LOANS, (*LV, LT, Total, Reserve)
+
+### Reserving `&mut` borrowed pointers
+
+Unlike other borrowed pointers, `&mut` pointers are unaliasable,
+so we can reserve them like everything else:
+
+    Reserve-Mut-Borrowed-Ptr:
+      Type(LV) = &LT_P mut Ty
+      RESERVE(LV, LT) = LOANS
+      ------------------------------------------------------------
+      RESERVE(*LV, LT) = LOANS, (*LV, LT, Total, Reserve)
+
+## Borrows in calls
+
+Earlier we said that the MUTATE, FREEZE, and ALIAS functions were used
+to compute the loans resulting from a borrow expression.  But this is
+not strictly correct, there is a slight complication that occurs with
+calls by which additional loans may be necessary.  We will explain
+that here and give the full details.
+
+Imagine a call expression `'a: E1(E2, E3)`, where `Ei` are some
+expressions. If we break this down to something a bit lower-level, it
+is kind of short for:
+
+    'a: {
+        'a_arg1: let temp1: ... = E1;
+        'a_arg2: let temp2: ... = E2;
+        'a_arg3: let temp3: ... = E3;
+        'a_call: temp1(temp2, temp3)
+    }
+
+Here the lifetime labels indicate the various lifetimes. As you can
+see there are in fact four relevant lifetimes (only one of which was
+named by the user): `'a` corresponds to the expression `E1(E2, E3)` as
+a whole. `'a_arg1`, `'a_arg2`, and `'a_arg3` correspond to the
+evaluations of `E1`, `E2`, and `E3` respectively. Finally, `'a_call`
+corresponds to the *actual call*, which is the point where the values
+of the parameters will be used.
+
+Now, let's look at a (contrived, but representative) example to see
+why all this matters:
+
+    struct Foo { f: uint, g: uint }
+    ...
+    fn add(p: &mut uint, v: uint) {
+        *p += v;
+    }
+    ...
+    fn inc(p: &mut uint) -> uint {
+        *p += 1; *p
+    }
+    fn weird() {
+        let mut x: ~Foo = ~Foo { ... };
+        'a: add(&mut (*x).f,
+                'b: inc(&mut (*x).f)) // (*)
+    }
+
+The important part is the line marked `(*)` which contains a call to
+`add()`. The first argument is a mutable borrow of the field `f`.
+The second argument *always borrows* the field `f`. Now, if these two
+borrows overlapped in time, this would be illegal, because there would
+be two `&mut` pointers pointing at `f`. And, in a way, they *do*
+overlap in time, since the first argument will be evaluated first,
+meaning that the pointer will exist when the second argument executes.
+But in another important way they do not overlap in time. Let's
+expand out that final call to `add()` as we did before:
+
+    'a: {
+        'a_arg1: let a_temp1: ... = add;
+        'a_arg2: let a_temp2: &'a_call mut uint = &'a_call mut (*x).f;
+        'a_arg3_: let a_temp3: uint = {
+            let b_temp1: ... = inc;
+            let b_temp2: &'b_call = &'b_call mut (*x).f;
+            'b_call: b_temp1(b_temp2)
+        };
+        'a_call: a_temp1(a_temp2, a_temp3)
+    }
+
+When it's written this way, we can see that although there are two
+borrows, the first has lifetime `'a_call` and the second has lifetime
+`'b_call` and in fact these lifetimes do not overlap. So everything
+is fine.
+
+But this does not mean that there isn't reason for caution!  Imagine a
+devious program like *this* one:
+
+    struct Foo { f: uint, g: uint }
+    ...
+    fn add(p: &mut uint, v: uint) {
+        *p += v;
+    }
+    ...
+    fn consume(x: ~Foo) -> uint {
+        x.f + x.g
+    }
+    fn weird() {
+        let mut x: ~Foo = ~Foo { ... };
+        'a: add(&mut (*x).f, consume(x)) // (*)
+    }
+
+In this case, there is only one borrow, but the second argument is
+`consume(x)` instead of a second borrow. Because `consume()` is
+declared to take a `~Foo`, it will in fact free the pointer `x` when
+it has finished executing. If it is not obvious why this is
+troublesome, consider this expanded version of that call:
+
+    'a: {
+        'a_arg1: let a_temp1: ... = add;
+        'a_arg2: let a_temp2: &'a_call mut uint = &'a_call mut (*x).f;
+        'a_arg3_: let a_temp3: uint = {
+            let b_temp1: ... = consume;
+            let b_temp2: ~Foo = x;
+            'b_call: b_temp1(x)
+        };
+        'a_call: a_temp1(a_temp2, a_temp3)
+    }
+
+In this example, we will have borrowed the first argument before `x`
+is freed and then free `x` during evaluation of the second
+argument. This causes `a_temp2` to be invalidated.
+
+Of course the loans computed from the borrow expression are supposed
+to prevent this situation.  But if we just considered the loans from
+`MUTATE((*x).f, 'a_call, Total)`, the resulting loans would be:
+
+    ((*x).f, 'a_call, Total,   Mut)
+    (*x,     'a_call, Partial, Mut)
+    (x,      'a_call, Partial, Mut)
+
+Because these loans are only in scope for `'a_call`, they do nothing
+to prevent the move that occurs evaluating the second argument.
+
+The way that we solve this is to say that if you have a borrow
+expression `&'LT_P mut LV` which itself occurs in the lifetime
+`'LT_B`, then the resulting loans are:
+
+    MUTATE(LV, LT_P, Total) + ALIAS(LV, LUB(LT_P, LT_B), Total)
+
+The call to MUTATE is what we've seen so far.  The second part
+expresses the idea that the expression LV will be evaluated starting
+at LT_B until the end of LT_P.  Now, in the normal case, LT_P >= LT_B,
+and so the second set of loans that result from a ALIAS are basically
+a no-op.  However, in the case of an argument where the evaluation of
+the borrow occurs before the interval where the resulting pointer will
+be used, this ALIAS is important.
+
+In the case of our example, it would produce a set of loans like:
+
+    ((*x).f, 'a, Total, Const)
+    (*x, 'a, Total, Const)
+    (x, 'a, Total, Imm)
+
+The scope of these loans is `'a = LUB('a_arg2, 'a_call)`, and so they
+encompass all subsequent arguments.  The first set of loans are Const
+loans, which basically just prevent moves.  However, when we cross
+over the dereference of the owned pointer `x`, the rule for ALIAS
+specifies that `x` must be frozen, and hence the final loan is an Imm
+loan.  In any case the troublesome second argument would be flagged
+as an error.
+
+# Maps that are created
+
+Borrowck results in two maps.
+
+- `root_map`: identifies those expressions or patterns whose result
+  needs to be rooted. Conceptually the root_map maps from an
+  expression or pattern node to a `node_id` identifying the scope for
+  which the expression must be rooted (this `node_id` should identify
+  a block or call). The actual key to the map is not an expression id,
+  however, but a `root_map_key`, which combines an expression id with a
+  deref count and is used to cope with auto-deref.
+
+*/
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
deleted file mode 100644
index e40d0e63eb3..00000000000
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ /dev/null
@@ -1,643 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ----------------------------------------------------------------------
-// Gathering loans
-//
-// The borrow check proceeds in two phases. In phase one, we gather the full
-// set of loans that are required at any point.  These are sorted according to
-// their associated scopes.  In phase two, checking loans, we will then make
-// sure that all of these loans are honored.
-
-use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure};
-use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
-use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze,
-                       TotalTake, PartialTake, Immobile};
-use middle::borrowck::ReqMaps;
-use middle::borrowck::loan;
-use middle::mem_categorization::{cmt, mem_categorization_ctxt};
-use middle::pat_util;
-use middle::ty::{ty_region};
-use middle::ty;
-use util::common::indenter;
-use util::ppaux::{Repr, region_to_str};
-
-use core::hashmap::{HashSet, HashMap};
-use syntax::ast::{m_const, m_imm, m_mutbl};
-use syntax::ast;
-use syntax::codemap::span;
-use syntax::print::pprust;
-use syntax::visit;
-
-/// Context used while gathering loans:
-///
-/// - `bccx`: the the borrow check context
-/// - `req_maps`: the maps computed by `gather_loans()`, see def'n of the
-///   struct `ReqMaps` for more info
-/// - `item_ub`: the id of the block for the enclosing fn/method item
-/// - `root_ub`: the id of the outermost block for which we can root
-///   an `@T`.  This is the id of the innermost enclosing
-///   loop or function body.
-///
-/// The role of `root_ub` is to prevent us from having to accumulate
-/// vectors of rooted items at runtime.  Consider this case:
-///
-///     fn foo(...) -> int {
-///         let mut ptr: &int;
-///         while some_cond {
-///             let x: @int = ...;
-///             ptr = &*x;
-///         }
-///         *ptr
-///     }
-///
-/// If we are not careful here, we would infer the scope of the borrow `&*x`
-/// to be the body of the function `foo()` as a whole.  We would then
-/// have root each `@int` that is produced, which is an unbounded number.
-/// No good.  Instead what will happen is that `root_ub` will be set to the
-/// body of the while loop and we will refuse to root the pointer `&*x`
-/// because it would have to be rooted for a region greater than `root_ub`.
-struct GatherLoanCtxt {
-    bccx: @BorrowckCtxt,
-    req_maps: ReqMaps,
-    item_ub: ast::node_id,
-    root_ub: ast::node_id,
-    ignore_adjustments: HashSet<ast::node_id>
-}
-
-pub fn gather_loans(bccx: @BorrowckCtxt, crate: @ast::crate) -> ReqMaps {
-    let glcx = @mut GatherLoanCtxt {
-        bccx: bccx,
-        req_maps: ReqMaps { req_loan_map: HashMap::new(),
-                            pure_map: HashMap::new() },
-        item_ub: 0,
-        root_ub: 0,
-        ignore_adjustments: HashSet::new()
-    };
-    let v = visit::mk_vt(@visit::Visitor {visit_expr: req_loans_in_expr,
-                                          visit_fn: req_loans_in_fn,
-                                          visit_stmt: add_stmt_to_map,
-                                          .. *visit::default_visitor()});
-    visit::visit_crate(crate, glcx, v);
-    let @GatherLoanCtxt{req_maps, _} = glcx;
-    return req_maps;
-}
-
-fn req_loans_in_fn(fk: &visit::fn_kind,
-                   decl: &ast::fn_decl,
-                   body: &ast::blk,
-                   sp: span,
-                   id: ast::node_id,
-                   self: @mut GatherLoanCtxt,
-                   v: visit::vt<@mut GatherLoanCtxt>) {
-    // see explanation attached to the `root_ub` field:
-    let old_item_id = self.item_ub;
-    let old_root_ub = self.root_ub;
-    self.root_ub = body.node.id;
-
-    match *fk {
-        visit::fk_anon(*) | visit::fk_fn_block(*) => {}
-        visit::fk_item_fn(*) | visit::fk_method(*) |
-        visit::fk_dtor(*) => {
-            self.item_ub = body.node.id;
-        }
-    }
-
-    visit::visit_fn(fk, decl, body, sp, id, self, v);
-    self.root_ub = old_root_ub;
-    self.item_ub = old_item_id;
-}
-
-fn req_loans_in_expr(ex: @ast::expr,
-                     self: @mut GatherLoanCtxt,
-                     vt: visit::vt<@mut GatherLoanCtxt>) {
-    let bccx = self.bccx;
-    let tcx = bccx.tcx;
-    let old_root_ub = self.root_ub;
-
-    debug!("req_loans_in_expr(expr=%?/%s)",
-           ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
-
-    // If this expression is borrowed, have to ensure it remains valid:
-    {
-        let mut this = &mut *self;
-        if !this.ignore_adjustments.contains(&ex.id) {
-            for tcx.adjustments.find(&ex.id).each |&adjustments| {
-                this.guarantee_adjustments(ex, *adjustments);
-            }
-        }
-    }
-
-    // Special checks for various kinds of expressions:
-    match ex.node {
-      ast::expr_addr_of(mutbl, base) => {
-        let base_cmt = self.bccx.cat_expr(base);
-
-        // make sure that the thing we are pointing out stays valid
-        // for the lifetime `scope_r` of the resulting ptr:
-        let scope_r = ty_region(tcx, ex.span, tcx.ty(ex));
-        self.guarantee_valid(base_cmt, mutbl, scope_r);
-        visit::visit_expr(ex, self, vt);
-      }
-
-      ast::expr_match(ex_v, ref arms) => {
-        let cmt = self.bccx.cat_expr(ex_v);
-        for (*arms).each |arm| {
-            for arm.pats.each |pat| {
-                self.gather_pat(cmt, *pat, arm.body.node.id, ex.id);
-            }
-        }
-        visit::visit_expr(ex, self, vt);
-      }
-
-      ast::expr_index(rcvr, _) |
-      ast::expr_binary(_, rcvr, _) |
-      ast::expr_unary(_, rcvr) |
-      ast::expr_assign_op(_, rcvr, _)
-      if self.bccx.method_map.contains_key(&ex.id) => {
-        // Receivers in method calls are always passed by ref.
-        //
-        // Here, in an overloaded operator, the call is this expression,
-        // and hence the scope of the borrow is this call.
-        //
-        // FIX? / NOT REALLY---technically we should check the other
-        // argument and consider the argument mode.  But how annoying.
-        // And this problem when goes away when argument modes are
-        // phased out.  So I elect to leave this undone.
-        let scope_r = ty::re_scope(ex.id);
-        let rcvr_cmt = self.bccx.cat_expr(rcvr);
-        self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
-
-        // FIXME (#3387): Total hack: Ignore adjustments for the left-hand
-        // side. Their regions will be inferred to be too large.
-        self.ignore_adjustments.insert(rcvr.id);
-
-        visit::visit_expr(ex, self, vt);
-      }
-
-      // FIXME--#3387
-      // ast::expr_binary(_, lhs, rhs) => {
-      //     // Universal comparison operators like ==, >=, etc
-      //     // take their arguments by reference.
-      //     let lhs_ty = ty::expr_ty(self.tcx(), lhs);
-      //     if !ty::type_is_scalar(lhs_ty) {
-      //         let scope_r = ty::re_scope(ex.id);
-      //         let lhs_cmt = self.bccx.cat_expr(lhs);
-      //         self.guarantee_valid(lhs_cmt, m_imm, scope_r);
-      //         let rhs_cmt = self.bccx.cat_expr(rhs);
-      //         self.guarantee_valid(rhs_cmt, m_imm, scope_r);
-      //     }
-      //     visit::visit_expr(ex, self, vt);
-      // }
-
-      ast::expr_field(rcvr, _, _)
-      if self.bccx.method_map.contains_key(&ex.id) => {
-        // Receivers in method calls are always passed by ref.
-        //
-        // Here, the field a.b is in fact a closure.  Eventually, this
-        // should be an &fn, but for now it's an @fn.  In any case,
-        // the enclosing scope is either the call where it is a rcvr
-        // (if used like `a.b(...)`), the call where it's an argument
-        // (if used like `x(a.b)`), or the block (if used like `let x
-        // = a.b`).
-        let scope_r = self.tcx().region_maps.encl_region(ex.id);
-        let rcvr_cmt = self.bccx.cat_expr(rcvr);
-        self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
-        visit::visit_expr(ex, self, vt);
-      }
-
-      // see explanation attached to the `root_ub` field:
-      ast::expr_while(cond, ref body) => {
-        // during the condition, can only root for the condition
-        self.root_ub = cond.id;
-        (vt.visit_expr)(cond, self, vt);
-
-        // during body, can only root for the body
-        self.root_ub = body.node.id;
-        (vt.visit_block)(body, self, vt);
-      }
-
-      // see explanation attached to the `root_ub` field:
-      ast::expr_loop(ref body, _) => {
-        self.root_ub = body.node.id;
-        visit::visit_expr(ex, self, vt);
-      }
-
-      _ => {
-        visit::visit_expr(ex, self, vt);
-      }
-    }
-
-    // Check any contained expressions:
-
-    self.root_ub = old_root_ub;
-}
-
-pub impl GatherLoanCtxt {
-    fn tcx(&mut self) -> ty::ctxt { self.bccx.tcx }
-
-    fn guarantee_adjustments(&mut self,
-                             expr: @ast::expr,
-                             adjustment: &ty::AutoAdjustment) {
-        debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
-               expr.repr(self.tcx()), adjustment);
-        let _i = indenter();
-
-        match *adjustment {
-            ty::AutoAddEnv(*) => {
-                debug!("autoaddenv -- no autoref");
-                return;
-            }
-
-            ty::AutoDerefRef(
-                ty::AutoDerefRef {
-                    autoref: None, _ }) => {
-                debug!("no autoref");
-                return;
-            }
-
-            ty::AutoDerefRef(
-                ty::AutoDerefRef {
-                    autoref: Some(ref autoref),
-                    autoderefs: autoderefs}) => {
-                let mcx = &mem_categorization_ctxt {
-                    tcx: self.tcx(),
-                    method_map: self.bccx.method_map};
-                let cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
-                debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
-
-                match autoref.kind {
-                    ty::AutoPtr => {
-                        self.guarantee_valid(cmt,
-                                             autoref.mutbl,
-                                             autoref.region)
-                    }
-                    ty::AutoBorrowVec | ty::AutoBorrowVecRef => {
-                        let cmt_index = mcx.cat_index(expr, cmt);
-                        self.guarantee_valid(cmt_index,
-                                             autoref.mutbl,
-                                             autoref.region)
-                    }
-                    ty::AutoBorrowFn => {
-                        let cmt_deref = mcx.cat_deref_fn(expr, cmt, 0);
-                        self.guarantee_valid(cmt_deref,
-                                             autoref.mutbl,
-                                             autoref.region)
-                    }
-                }
-            }
-        }
-    }
-
-    // guarantees that addr_of(cmt) will be valid for the duration of
-    // `static_scope_r`, or reports an error.  This may entail taking
-    // out loans, which will be added to the `req_loan_map`.  This can
-    // also entail "rooting" GC'd pointers, which means ensuring
-    // dynamically that they are not freed.
-    fn guarantee_valid(&mut self,
-                       cmt: cmt,
-                       req_mutbl: ast::mutability,
-                       scope_r: ty::Region)
-    {
-
-        let loan_kind = match req_mutbl {
-            m_mutbl => TotalTake,
-            m_imm => TotalFreeze,
-            m_const => Immobile
-        };
-
-        self.bccx.stats.guaranteed_paths += 1;
-
-        debug!("guarantee_valid(cmt=%s, req_mutbl=%?, \
-                loan_kind=%?, scope_r=%s)",
-               self.bccx.cmt_to_repr(cmt),
-               req_mutbl,
-               loan_kind,
-               region_to_str(self.tcx(), scope_r));
-        let _i = indenter();
-
-        match cmt.lp {
-          // If this expression is a loanable path, we MUST take out a
-          // loan.  This is somewhat non-obvious.  You might think,
-          // for example, that if we have an immutable local variable
-          // `x` whose value is being borrowed, we could rely on `x`
-          // not to change.  This is not so, however, because even
-          // immutable locals can be moved.  So we take out a loan on
-          // `x`, guaranteeing that it remains immutable for the
-          // duration of the reference: if there is an attempt to move
-          // it within that scope, the loan will be detected and an
-          // error will be reported.
-          Some(_) => {
-              match loan::loan(self.bccx, cmt, scope_r, loan_kind) {
-                  Err(ref e) => { self.bccx.report((*e)); }
-                  Ok(loans) => {
-                      self.add_loans(cmt, loan_kind, scope_r, loans);
-                  }
-              }
-          }
-
-          // The path is not loanable: in that case, we must try and
-          // preserve it dynamically (or see that it is preserved by
-          // virtue of being rooted in some immutable path).  We must
-          // also check that the mutability of the desired pointer
-          // matches with the actual mutability (but if an immutable
-          // pointer is desired, that is ok as long as we are pure)
-          None => {
-            let result: bckres<PreserveCondition> = {
-                do self.check_mutbl(loan_kind, cmt).chain |pc1| {
-                    do self.bccx.preserve(cmt, scope_r,
-                                          self.item_ub,
-                                          self.root_ub).chain |pc2| {
-                        Ok(pc1.combine(pc2))
-                    }
-                }
-            };
-
-            match result {
-                Ok(PcOk) => {
-                    debug!("result of preserve: PcOk");
-
-                    // we were able guarantee the validity of the ptr,
-                    // perhaps by rooting or because it is immutably
-                    // rooted.  good.
-                    self.bccx.stats.stable_paths += 1;
-                }
-                Ok(PcIfPure(ref e)) => {
-                    debug!("result of preserve: %?", PcIfPure((*e)));
-
-                    // we are only able to guarantee the validity if
-                    // the scope is pure
-                    match scope_r {
-                        ty::re_scope(pure_id) => {
-                            // if the scope is some block/expr in the
-                            // fn, then just require that this scope
-                            // be pure
-                            self.req_maps.pure_map.insert(pure_id, *e);
-                            self.bccx.stats.req_pure_paths += 1;
-
-                            debug!("requiring purity for scope %?",
-                                   scope_r);
-
-                            if self.tcx().sess.borrowck_note_pure() {
-                                self.bccx.span_note(
-                                    cmt.span,
-                                    fmt!("purity required"));
-                            }
-                        }
-                        _ => {
-                            // otherwise, we can't enforce purity for
-                            // that scope, so give up and report an
-                            // error
-                            self.bccx.report((*e));
-                        }
-                    }
-                }
-                Err(ref e) => {
-                    // we cannot guarantee the validity of this pointer
-                    debug!("result of preserve: error");
-                    self.bccx.report((*e));
-                }
-            }
-          }
-        }
-    }
-
-    // Check that the pat `cmt` is compatible with the required
-    // mutability, presuming that it can be preserved to stay alive
-    // long enough.
-    //
-    // For example, if you have an expression like `&x.f` where `x`
-    // has type `@mut{f:int}`, this check might fail because `&x.f`
-    // reqires an immutable pointer, but `f` lives in (aliased)
-    // mutable memory.
-    fn check_mutbl(&mut self,
-                   loan_kind: LoanKind,
-                   cmt: cmt)
-                -> bckres<PreserveCondition> {
-        debug!("check_mutbl(loan_kind=%?, cmt.mutbl=%?)",
-               loan_kind, cmt.mutbl);
-
-        match loan_kind {
-            Immobile => Ok(PcOk),
-
-            TotalTake | PartialTake => {
-                if cmt.mutbl.is_mutable() {
-                    Ok(PcOk)
-                } else {
-                    Err(bckerr { cmt: cmt, code: err_mutbl(loan_kind) })
-                }
-            }
-
-            TotalFreeze | PartialFreeze => {
-                if cmt.mutbl.is_immutable() {
-                    Ok(PcOk)
-                } else if cmt.cat.is_mutable_box() {
-                    Ok(PcOk)
-                } else {
-                    // Eventually:
-                    let e = bckerr {cmt: cmt,
-                                    code: err_mutbl(loan_kind)};
-                    Ok(PcIfPure(e))
-                }
-            }
-        }
-    }
-
-    fn add_loans(&mut self,
-                 cmt: cmt,
-                 loan_kind: LoanKind,
-                 scope_r: ty::Region,
-                 loans: ~[Loan]) {
-        if loans.len() == 0 {
-            return;
-        }
-
-        // Normally we wouldn't allow `re_free` here. However, in this case
-        // it should be sound. Below is nmatsakis' reasoning:
-        //
-        // Perhaps [this permits] a function kind of like this one here, which
-        // consumes one mut pointer and returns a narrower one:
-        //
-        //     struct Foo { f: int }
-        //     fn foo(p: &'v mut Foo) -> &'v mut int { &mut p.f }
-        //
-        // I think this should work fine but there is more subtlety to it than
-        // I at first imagined. Unfortunately it's a very important use case,
-        // I think, so it really ought to work. The changes you [pcwalton]
-        // made to permit re_free() do permit this case, I think, but I'm not
-        // sure what else they permit. I have to think that over a bit.
-        //
-        // Ordinarily, a loan with scope re_free wouldn't make sense, because
-        // you couldn't enforce it. But in this case, your function signature
-        // informs the caller that you demand exclusive access to p and its
-        // contents for the lifetime v. Since borrowed pointers are
-        // non-copyable, they must have (a) made a borrow which will enforce
-        // those conditions and then (b) given you the resulting pointer.
-        // Therefore, they should be respecting the loan. So it actually seems
-        // that it's ok in this case to have a loan with re_free, so long as
-        // the scope of the loan is no greater than the region pointer on
-        // which it is based. Neat but not something I had previously
-        // considered all the way through. (Note that we already rely on
-        // similar reasoning to permit you to return borrowed pointers into
-        // immutable structures, this is just the converse I suppose)
-
-        let scope_id = match scope_r {
-            ty::re_scope(scope_id) |
-            ty::re_free(ty::FreeRegion {scope_id, _}) => {
-                scope_id
-            }
-            _ => {
-                self.bccx.tcx.sess.span_bug(
-                    cmt.span,
-                    fmt!("loans required but scope is scope_region is %s \
-                          (%?)",
-                         region_to_str(self.tcx(), scope_r),
-                         scope_r));
-            }
-        };
-
-        self.add_loans_to_scope_id(scope_id, loans);
-
-        if loan_kind.is_freeze() && !cmt.mutbl.is_immutable() {
-            self.bccx.stats.loaned_paths_imm += 1;
-
-            if self.tcx().sess.borrowck_note_loan() {
-                self.bccx.span_note(
-                    cmt.span,
-                    fmt!("immutable loan required"));
-            }
-        } else {
-            self.bccx.stats.loaned_paths_same += 1;
-        }
-    }
-
-    fn add_loans_to_scope_id(&mut self,
-                             scope_id: ast::node_id,
-                             loans: ~[Loan]) {
-        debug!("adding %u loans to scope_id %?: %s",
-               loans.len(), scope_id,
-               str::connect(loans.map(|l| self.bccx.loan_to_repr(l)), ", "));
-        match self.req_maps.req_loan_map.find(&scope_id) {
-            Some(req_loans) => {
-                req_loans.push_all(loans);
-                return;
-            }
-            None => {}
-        }
-        self.req_maps.req_loan_map.insert(scope_id, @mut loans);
-    }
-
-    fn gather_pat(@mut self,
-                  discr_cmt: cmt,
-                  root_pat: @ast::pat,
-                  arm_id: ast::node_id,
-                  match_id: ast::node_id) {
-        do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
-            match pat.node {
-              ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
-                match bm {
-                  ast::bind_by_ref(mutbl) => {
-                    // ref x or ref x @ p --- creates a ptr which must
-                    // remain valid for the scope of the match
-
-                    // find the region of the resulting pointer (note that
-                    // the type of such a pattern will *always* be a
-                    // region pointer)
-                    let scope_r = ty_region(self.tcx(), pat.span,
-                                            self.tcx().ty(pat));
-
-                    // if the scope of the region ptr turns out to be
-                    // specific to this arm, wrap the categorization with
-                    // a cat_discr() node.  There is a detailed discussion
-                    // of the function of this node in method preserve():
-                    let arm_scope = ty::re_scope(arm_id);
-                    if self.bccx.is_subregion_of(scope_r, arm_scope) {
-                        let cmt_discr = self.bccx.cat_discr(cmt, match_id);
-                        self.guarantee_valid(cmt_discr, mutbl, scope_r);
-                    } else {
-                        self.guarantee_valid(cmt, mutbl, scope_r);
-                    }
-                  }
-                  ast::bind_by_copy | ast::bind_infer => {
-                    // Nothing to do here; neither copies nor moves induce
-                    // borrows.
-                  }
-                }
-              }
-
-              ast::pat_vec(_, Some(slice_pat), _) => {
-                  // The `slice_pat` here creates a slice into the
-                  // original vector.  This is effectively a borrow of
-                  // the elements of the vector being matched.
-
-                  let slice_ty = self.tcx().ty(slice_pat);
-                  let (slice_mutbl, slice_r) =
-                      self.vec_slice_info(slice_pat, slice_ty);
-                  let mcx = self.bccx.mc_ctxt();
-                  let cmt_index = mcx.cat_index(slice_pat, cmt);
-                  self.guarantee_valid(cmt_index, slice_mutbl, slice_r);
-              }
-
-              _ => {}
-            }
-        }
-    }
-
-    fn vec_slice_info(@mut self,
-                      pat: @ast::pat,
-                      slice_ty: ty::t) -> (ast::mutability, ty::Region) {
-        /*!
-         *
-         * In a pattern like [a, b, ..c], normally `c` has slice type,
-         * but if you have [a, b, ..ref c], then the type of `ref c`
-         * will be `&&[]`, so to extract the slice details we have
-         * to recurse through rptrs.
-         */
-
-        match ty::get(slice_ty).sty {
-            ty::ty_evec(slice_mt, ty::vstore_slice(slice_r)) => {
-                (slice_mt.mutbl, slice_r)
-            }
-
-            ty::ty_rptr(_, ref mt) => {
-                self.vec_slice_info(pat, mt.ty)
-            }
-
-            _ => {
-                self.tcx().sess.span_bug(
-                    pat.span,
-                    fmt!("Type of slice pattern is not a slice"));
-            }
-        }
-    }
-
-    fn pat_is_variant_or_struct(@mut self, pat: @ast::pat) -> bool {
-        pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat)
-    }
-
-    fn pat_is_binding(@mut self, pat: @ast::pat) -> bool {
-        pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)
-    }
-}
-
-// Setting up info that preserve needs.
-// This is just the most convenient place to do it.
-fn add_stmt_to_map(stmt: @ast::stmt,
-                   self: @mut GatherLoanCtxt,
-                   vt: visit::vt<@mut GatherLoanCtxt>) {
-    match stmt.node {
-        ast::stmt_expr(_, id) | ast::stmt_semi(_, id) => {
-            self.bccx.stmt_map.insert(id);
-        }
-        _ => ()
-    }
-    visit::visit_stmt(stmt, self, vt);
-}
-
diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
new file mode 100644
index 00000000000..21d7e7041d9
--- /dev/null
+++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
@@ -0,0 +1,322 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This module implements the check that the lifetime of a borrow
+//! does not exceed the lifetime of the value being borrowed.
+
+use core::prelude::*;
+use middle::borrowck::*;
+use mc = middle::mem_categorization;
+use middle::ty;
+use syntax::ast::{m_const, m_imm, m_mutbl};
+use syntax::ast;
+use syntax::codemap::span;
+
+pub fn guarantee_lifetime(bccx: @BorrowckCtxt,
+                          item_scope_id: ast::node_id,
+                          root_scope_id: ast::node_id,
+                          span: span,
+                          cmt: mc::cmt,
+                          loan_region: ty::Region,
+                          loan_mutbl: ast::mutability) {
+    debug!("guarantee_lifetime(cmt=%s, loan_region=%s)",
+           cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx));
+    let ctxt = GuaranteeLifetimeContext {bccx: bccx,
+                                         item_scope_id: item_scope_id,
+                                         span: span,
+                                         loan_region: loan_region,
+                                         loan_mutbl: loan_mutbl,
+                                         cmt_original: cmt,
+                                         root_scope_id: root_scope_id};
+    ctxt.check(cmt, None);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Private
+
+struct GuaranteeLifetimeContext {
+    bccx: @BorrowckCtxt,
+
+    // the node id of the function body for the enclosing item
+    item_scope_id: ast::node_id,
+
+    // the node id of the innermost loop / function body; this is the
+    // longest scope for which we can root managed boxes
+    root_scope_id: ast::node_id,
+
+    span: span,
+    loan_region: ty::Region,
+    loan_mutbl: ast::mutability,
+    cmt_original: mc::cmt
+}
+
+impl GuaranteeLifetimeContext {
+    fn tcx(&self) -> ty::ctxt {
+        self.bccx.tcx
+    }
+
+    fn check(&self, cmt: mc::cmt, discr_scope: Option<ast::node_id>) {
+        //! Main routine. Walks down `cmt` until we find the "guarantor".
+
+        match cmt.cat {
+            mc::cat_rvalue |
+            mc::cat_implicit_self |
+            mc::cat_copied_upvar(*) |
+            mc::cat_local(*) |
+            mc::cat_arg(*) |
+            mc::cat_self(*) |
+            mc::cat_deref(_, _, mc::region_ptr(*)) |
+            mc::cat_deref(_, _, mc::unsafe_ptr) => {
+                let scope = self.scope(cmt);
+                self.check_scope(scope)
+            }
+
+            mc::cat_stack_upvar(cmt) => {
+                self.check(cmt, discr_scope)
+            }
+
+            mc::cat_static_item => {
+            }
+
+            mc::cat_deref(base, derefs, mc::gc_ptr(ptr_mutbl)) => {
+                let base_scope = self.scope(base);
+
+                // See rule Freeze-Imm-Managed-Ptr-2 in doc.rs
+                let omit_root = (
+                    self.bccx.is_subregion_of(self.loan_region, base_scope) &&
+                    base.mutbl.is_immutable() &&
+                    !self.is_moved(base)
+                );
+
+                if !omit_root {
+                    self.check_root(base, derefs, ptr_mutbl, discr_scope);
+                } else {
+                    debug!("omitting root, base=%s, base_scope=%?",
+                           base.repr(self.tcx()), base_scope);
+                }
+            }
+
+            mc::cat_deref(base, _, mc::uniq_ptr(*)) |
+            mc::cat_interior(base, _) => {
+                self.check(base, discr_scope)
+            }
+
+            mc::cat_discr(base, new_discr_scope) => {
+                // Subtle: in a match, we must ensure that each binding
+                // variable remains valid for the duration of the arm in
+                // which it appears, presuming that this arm is taken.
+                // But it is inconvenient in trans to root something just
+                // for one arm.  Therefore, we insert a cat_discr(),
+                // basically a special kind of category that says "if this
+                // value must be dynamically rooted, root it for the scope
+                // `match_id`.
+                //
+                // As an example, consider this scenario:
+                //
+                //    let mut x = @Some(3);
+                //    match *x { Some(y) {...} None {...} }
+                //
+                // Technically, the value `x` need only be rooted
+                // in the `some` arm.  However, we evaluate `x` in trans
+                // before we know what arm will be taken, so we just
+                // always root it for the duration of the match.
+                //
+                // As a second example, consider *this* scenario:
+                //
+                //    let x = @mut @Some(3);
+                //    match x { @@Some(y) {...} @@None {...} }
+                //
+                // Here again, `x` need only be rooted in the `some` arm.
+                // In this case, the value which needs to be rooted is
+                // found only when checking which pattern matches: but
+                // this check is done before entering the arm.  Therefore,
+                // even in this case we just choose to keep the value
+                // rooted for the entire match.  This means the value will be
+                // rooted even if the none arm is taken.  Oh well.
+                //
+                // At first, I tried to optimize the second case to only
+                // root in one arm, but the result was suboptimal: first,
+                // it interfered with the construction of phi nodes in the
+                // arm, as we were adding code to root values before the
+                // phi nodes were added.  This could have been addressed
+                // with a second basic block.  However, the naive approach
+                // also yielded suboptimal results for patterns like:
+                //
+                //    let x = @mut @...;
+                //    match x { @@some_variant(y) | @@some_other_variant(y) =>
+                //
+                // The reason is that we would root the value once for
+                // each pattern and not once per arm.  This is also easily
+                // fixed, but it's yet more code for what is really quite
+                // the corner case.
+                //
+                // Nonetheless, if you decide to optimize this case in the
+                // future, you need only adjust where the cat_discr()
+                // node appears to draw the line between what will be rooted
+                // in the *arm* vs the *match*.
+                self.check(base, Some(new_discr_scope))
+            }
+        }
+    }
+
+    fn check_root(&self,
+                  cmt_base: mc::cmt,
+                  derefs: uint,
+                  ptr_mutbl: ast::mutability,
+                  discr_scope: Option<ast::node_id>) {
+        debug!("check_root(cmt_base=%s, derefs=%? ptr_mutbl=%?, \
+                discr_scope=%?)",
+               cmt_base.repr(self.tcx()),
+               derefs,
+               ptr_mutbl,
+               discr_scope);
+
+        // Make sure that the loan does not exceed the maximum time
+        // that we can root the value, dynamically.
+        let root_region = ty::re_scope(self.root_scope_id);
+        if !self.bccx.is_subregion_of(self.loan_region, root_region) {
+            self.report_error(
+                err_out_of_root_scope(root_region, self.loan_region));
+            return;
+        }
+
+        // Extract the scope id that indicates how long the rooting is required
+        let root_scope = match self.loan_region {
+            ty::re_scope(id) => id,
+            _ => {
+                // the check above should fail for anything is not re_scope
+                self.bccx.tcx.sess.span_bug(
+                    cmt_base.span,
+                    fmt!("Cannot issue root for scope region: %?",
+                         self.loan_region));
+            }
+        };
+
+        // If inside of a match arm, expand the rooting to the entire
+        // match. See the detailed discussion in `check()` above.
+        let mut root_scope = match discr_scope {
+            None => root_scope,
+            Some(id) => {
+                if self.bccx.is_subscope_of(root_scope, id) {
+                    id
+                } else {
+                    root_scope
+                }
+            }
+        };
+
+        // FIXME(#3511) grow to the nearest cleanup scope---this can
+        // cause observable errors if freezing!
+        if !self.bccx.tcx.region_maps.is_cleanup_scope(root_scope) {
+            debug!("%? is not a cleanup scope, adjusting", root_scope);
+            root_scope = self.bccx.tcx.region_maps.cleanup_scope(root_scope);
+        }
+
+        // If we are borrowing the inside of an `@mut` box,
+        // we need to dynamically mark it to prevent incompatible
+        // borrows from happening later.
+        let opt_dyna = match ptr_mutbl {
+            m_imm | m_const => None,
+            m_mutbl => {
+                match self.loan_mutbl {
+                    m_mutbl => Some(DynaMut),
+                    m_imm | m_const => Some(DynaImm)
+                }
+            }
+        };
+
+        // Add a record of what is required
+        let rm_key = root_map_key {id: cmt_base.id, derefs: derefs};
+        let root_info = RootInfo {scope: root_scope, freeze: opt_dyna};
+        self.bccx.root_map.insert(rm_key, root_info);
+
+        debug!("root_key: %? root_info: %?", rm_key, root_info);
+    }
+
+    fn check_scope(&self, max_scope: ty::Region) {
+        //! Reports an error if `loan_region` is larger than `valid_scope`
+
+        if !self.bccx.is_subregion_of(self.loan_region, max_scope) {
+            self.report_error(err_out_of_scope(max_scope, self.loan_region));
+        }
+    }
+
+    fn is_moved(&self, cmt: mc::cmt) -> bool {
+        //! True if `cmt` is something that is potentially moved
+        //! out of the current stack frame.
+
+        match cmt.guarantor().cat {
+            mc::cat_local(id) |
+            mc::cat_self(id) |
+            mc::cat_arg(id, _) => {
+                self.bccx.moved_variables_set.contains(&id)
+            }
+            mc::cat_rvalue |
+            mc::cat_static_item |
+            mc::cat_implicit_self |
+            mc::cat_copied_upvar(*) |
+            mc::cat_deref(*) => {
+                false
+            }
+            r @ mc::cat_interior(*) |
+            r @ mc::cat_stack_upvar(*) |
+            r @ mc::cat_discr(*) => {
+                self.tcx().sess.span_bug(
+                    cmt.span,
+                    fmt!("illegal guarantor category: %?", r));
+            }
+        }
+    }
+
+    fn scope(&self, cmt: mc::cmt) -> ty::Region {
+        //! Returns the maximal region scope for the which the
+        //! lvalue `cmt` is guaranteed to be valid without any
+        //! rooting etc, and presuming `cmt` is not mutated.
+
+        match cmt.cat {
+            mc::cat_rvalue => {
+                ty::re_scope(self.bccx.tcx.region_maps.cleanup_scope(cmt.id))
+            }
+            mc::cat_implicit_self |
+            mc::cat_copied_upvar(_) => {
+                ty::re_scope(self.item_scope_id)
+            }
+            mc::cat_static_item => {
+                ty::re_static
+            }
+            mc::cat_local(local_id) |
+            mc::cat_arg(local_id, _) |
+            mc::cat_self(local_id) => {
+                self.bccx.tcx.region_maps.encl_region(local_id)
+            }
+            mc::cat_deref(_, _, mc::unsafe_ptr(*)) => {
+                ty::re_static
+            }
+            mc::cat_deref(_, _, mc::region_ptr(_, r)) => {
+                r
+            }
+            mc::cat_deref(cmt, _, mc::uniq_ptr(*)) |
+            mc::cat_deref(cmt, _, mc::gc_ptr(*)) |
+            mc::cat_interior(cmt, _) |
+            mc::cat_stack_upvar(cmt) |
+            mc::cat_discr(cmt, _) => {
+                self.scope(cmt)
+            }
+        }
+    }
+
+    fn report_error(&self, code: bckerr_code) {
+        self.bccx.report(BckError {
+            cmt: self.cmt_original,
+            span: self.span,
+            code: code
+        });
+    }
+}
diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs
new file mode 100644
index 00000000000..82638ceb4d4
--- /dev/null
+++ b/src/librustc/middle/borrowck/gather_loans/mod.rs
@@ -0,0 +1,710 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ----------------------------------------------------------------------
+// Gathering loans
+//
+// The borrow check proceeds in two phases. In phase one, we gather the full
+// set of loans that are required at any point.  These are sorted according to
+// their associated scopes.  In phase two, checking loans, we will then make
+// sure that all of these loans are honored.
+
+use core::prelude::*;
+
+use middle::borrowck::*;
+use mc = middle::mem_categorization;
+use middle::pat_util;
+use middle::ty::{ty_region};
+use middle::ty;
+use util::common::indenter;
+use util::ppaux::{Repr};
+
+use core::hashmap::HashSet;
+use core::vec;
+use syntax::ast::{m_const, m_imm, m_mutbl};
+use syntax::ast;
+use syntax::ast_util::id_range;
+use syntax::codemap::span;
+use syntax::print::pprust;
+use syntax::visit;
+
+mod lifetime;
+mod restrictions;
+
+/// Context used while gathering loans:
+///
+/// - `bccx`: the the borrow check context
+/// - `item_ub`: the id of the block for the enclosing fn/method item
+/// - `root_ub`: the id of the outermost block for which we can root
+///   an `@T`.  This is the id of the innermost enclosing
+///   loop or function body.
+///
+/// The role of `root_ub` is to prevent us from having to accumulate
+/// vectors of rooted items at runtime.  Consider this case:
+///
+///     fn foo(...) -> int {
+///         let mut ptr: &int;
+///         while some_cond {
+///             let x: @int = ...;
+///             ptr = &*x;
+///         }
+///         *ptr
+///     }
+///
+/// If we are not careful here, we would infer the scope of the borrow `&*x`
+/// to be the body of the function `foo()` as a whole.  We would then
+/// have root each `@int` that is produced, which is an unbounded number.
+/// No good.  Instead what will happen is that `root_ub` will be set to the
+/// body of the while loop and we will refuse to root the pointer `&*x`
+/// because it would have to be rooted for a region greater than `root_ub`.
+struct GatherLoanCtxt {
+    bccx: @BorrowckCtxt,
+    id_range: id_range,
+    all_loans: @mut ~[Loan],
+    item_ub: ast::node_id,
+    repeating_ids: ~[ast::node_id],
+    ignore_adjustments: HashSet<ast::node_id>
+}
+
+pub fn gather_loans(bccx: @BorrowckCtxt,
+                    body: &ast::blk) -> (id_range, @mut ~[Loan]) {
+    let glcx = @mut GatherLoanCtxt {
+        bccx: bccx,
+        id_range: id_range::max(),
+        all_loans: @mut ~[],
+        item_ub: body.node.id,
+        repeating_ids: ~[body.node.id],
+        ignore_adjustments: HashSet::new()
+    };
+    let v = visit::mk_vt(@visit::Visitor {visit_expr: gather_loans_in_expr,
+                                          visit_block: gather_loans_in_block,
+                                          visit_fn: gather_loans_in_fn,
+                                          visit_stmt: add_stmt_to_map,
+                                          visit_pat: add_pat_to_id_range,
+                                          .. *visit::default_visitor()});
+    (v.visit_block)(body, glcx, v);
+    return (glcx.id_range, glcx.all_loans);
+}
+
+fn add_pat_to_id_range(p: @ast::pat,
+                       self: @mut GatherLoanCtxt,
+                       v: visit::vt<@mut GatherLoanCtxt>) {
+    // NB: This visitor function just adds the pat ids into the id
+    // range. We gather loans that occur in patterns using the
+    // `gather_pat()` method below. Eventually these two should be
+    // brought together.
+    self.id_range.add(p.id);
+    visit::visit_pat(p, self, v);
+}
+
+fn gather_loans_in_fn(fk: &visit::fn_kind,
+                      decl: &ast::fn_decl,
+                      body: &ast::blk,
+                      sp: span,
+                      id: ast::node_id,
+                      self: @mut GatherLoanCtxt,
+                      v: visit::vt<@mut GatherLoanCtxt>) {
+    match fk {
+        // Do not visit items here, the outer loop in borrowck/mod
+        // will visit them for us in turn.
+        &visit::fk_item_fn(*) | &visit::fk_method(*) |
+        &visit::fk_dtor(*) => {
+            return;
+        }
+
+        // Visit closures as part of the containing item.
+        &visit::fk_anon(*) | &visit::fk_fn_block(*) => {
+            self.push_repeating_id(body.node.id);
+            visit::visit_fn(fk, decl, body, sp, id, self, v);
+            self.pop_repeating_id(body.node.id);
+        }
+    }
+}
+
+fn gather_loans_in_block(blk: &ast::blk,
+                         self: @mut GatherLoanCtxt,
+                         vt: visit::vt<@mut GatherLoanCtxt>) {
+    self.id_range.add(blk.node.id);
+    visit::visit_block(blk, self, vt);
+}
+
+fn gather_loans_in_expr(ex: @ast::expr,
+                        self: @mut GatherLoanCtxt,
+                        vt: visit::vt<@mut GatherLoanCtxt>) {
+    let bccx = self.bccx;
+    let tcx = bccx.tcx;
+
+    debug!("gather_loans_in_expr(expr=%?/%s)",
+           ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
+
+    self.id_range.add(ex.id);
+    self.id_range.add(ex.callee_id);
+
+    // If this expression is borrowed, have to ensure it remains valid:
+    {
+        let mut this = &mut *self; // FIXME(#5074)
+        if !this.ignore_adjustments.contains(&ex.id) {
+            for tcx.adjustments.find(&ex.id).each |&adjustments| {
+                this.guarantee_adjustments(ex, *adjustments);
+            }
+        }
+    }
+
+    // Special checks for various kinds of expressions:
+    match ex.node {
+      ast::expr_addr_of(mutbl, base) => {
+        let base_cmt = self.bccx.cat_expr(base);
+
+        // make sure that the thing we are pointing out stays valid
+        // for the lifetime `scope_r` of the resulting ptr:
+        let scope_r = ty_region(tcx, ex.span, ty::expr_ty(tcx, ex));
+        self.guarantee_valid(ex.id, ex.span, base_cmt, mutbl, scope_r);
+        visit::visit_expr(ex, self, vt);
+      }
+
+      ast::expr_call(f, ref args, _) => {
+        let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));
+        self.guarantee_arguments(ex, *args, arg_tys);
+        visit::visit_expr(ex, self, vt);
+      }
+
+      ast::expr_method_call(_, _, _, ref args, _) => {
+        let arg_tys = ty::ty_fn_args(ty::node_id_to_type(self.tcx(),
+                                                         ex.callee_id));
+        self.guarantee_arguments(ex, *args, arg_tys);
+
+        visit::visit_expr(ex, self, vt);
+      }
+
+      ast::expr_match(ex_v, ref arms) => {
+        let cmt = self.bccx.cat_expr(ex_v);
+        for arms.each |arm| {
+            for arm.pats.each |pat| {
+                self.gather_pat(cmt, *pat, arm.body.node.id, ex.id);
+            }
+        }
+        visit::visit_expr(ex, self, vt);
+      }
+
+      ast::expr_index(rcvr, _) |
+      ast::expr_binary(_, rcvr, _) |
+      ast::expr_unary(_, rcvr) |
+      ast::expr_assign_op(_, rcvr, _)
+      if self.bccx.method_map.contains_key(&ex.id) => {
+        // Receivers in method calls are always passed by ref.
+        //
+        // Here, in an overloaded operator, the call is this expression,
+        // and hence the scope of the borrow is this call.
+        //
+        // FIX? / NOT REALLY---technically we should check the other
+        // argument and consider the argument mode.  But how annoying.
+        // And this problem when goes away when argument modes are
+        // phased out.  So I elect to leave this undone.
+        let scope_r = ty::re_scope(ex.id);
+        let rcvr_cmt = self.bccx.cat_expr(rcvr);
+        self.guarantee_valid(rcvr.id, rcvr.span, rcvr_cmt, m_imm, scope_r);
+
+        // FIXME (#3387): Total hack: Ignore adjustments for the left-hand
+        // side. Their regions will be inferred to be too large.
+        self.ignore_adjustments.insert(rcvr.id);
+
+        visit::visit_expr(ex, self, vt);
+      }
+
+      // FIXME--#3387
+      // ast::expr_binary(_, lhs, rhs) => {
+      //     // Universal comparison operators like ==, >=, etc
+      //     // take their arguments by reference.
+      //     let lhs_ty = ty::expr_ty(self.tcx(), lhs);
+      //     if !ty::type_is_scalar(lhs_ty) {
+      //         let scope_r = ty::re_scope(ex.id);
+      //         let lhs_cmt = self.bccx.cat_expr(lhs);
+      //         self.guarantee_valid(lhs_cmt, m_imm, scope_r);
+      //         let rhs_cmt = self.bccx.cat_expr(rhs);
+      //         self.guarantee_valid(rhs_cmt, m_imm, scope_r);
+      //     }
+      //     visit::visit_expr(ex, self, vt);
+      // }
+
+      // see explanation attached to the `root_ub` field:
+      ast::expr_while(cond, ref body) => {
+          // during the condition, can only root for the condition
+          self.push_repeating_id(cond.id);
+          (vt.visit_expr)(cond, self, vt);
+          self.pop_repeating_id(cond.id);
+
+          // during body, can only root for the body
+          self.push_repeating_id(body.node.id);
+          (vt.visit_block)(body, self, vt);
+          self.pop_repeating_id(body.node.id);
+      }
+
+      // see explanation attached to the `root_ub` field:
+      ast::expr_loop(ref body, _) => {
+          self.push_repeating_id(body.node.id);
+          visit::visit_expr(ex, self, vt);
+          self.pop_repeating_id(body.node.id);
+      }
+
+      _ => {
+        visit::visit_expr(ex, self, vt);
+      }
+    }
+}
+
+pub impl GatherLoanCtxt {
+    fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
+
+    fn push_repeating_id(&mut self, id: ast::node_id) {
+        self.repeating_ids.push(id);
+    }
+
+    fn pop_repeating_id(&mut self, id: ast::node_id) {
+        let popped = self.repeating_ids.pop();
+        assert!(id == popped);
+    }
+
+    fn guarantee_arguments(&mut self,
+                           call_expr: @ast::expr,
+                           args: &[@ast::expr],
+                           arg_tys: &[ty::arg]) {
+        for vec::each2(args, arg_tys) |arg, arg_ty| {
+            match ty::resolved_mode(self.tcx(), arg_ty.mode) {
+                ast::by_ref => {
+                    self.guarantee_by_ref_argument(call_expr, *arg);
+                }
+                ast::by_copy => {}
+            }
+        }
+    }
+
+    fn guarantee_by_ref_argument(&mut self,
+                                 call_expr: @ast::expr,
+                                 arg_expr: @ast::expr) {
+        // FIXME(#5074) nested method calls
+        let scope_r = ty::re_scope(call_expr.id);
+        let arg_cmt = self.bccx.cat_expr(arg_expr);
+        self.guarantee_valid(arg_expr.id, arg_expr.span,
+                             arg_cmt, m_imm, scope_r);
+    }
+
+    fn guarantee_adjustments(&mut self,
+                             expr: @ast::expr,
+                             adjustment: &ty::AutoAdjustment) {
+        debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
+               expr.repr(self.tcx()), adjustment);
+        let _i = indenter();
+
+        match *adjustment {
+            ty::AutoAddEnv(*) => {
+                debug!("autoaddenv -- no autoref");
+                return;
+            }
+
+            ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoref: None, _ }) => {
+                debug!("no autoref");
+                return;
+            }
+
+            ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoref: Some(ref autoref),
+                    autoderefs: autoderefs}) => {
+                let mcx = &mc::mem_categorization_ctxt {
+                    tcx: self.tcx(),
+                    method_map: self.bccx.method_map};
+                let mut cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
+                debug!("after autoderef, cmt=%s", cmt.repr(self.tcx()));
+
+                match *autoref {
+                    ty::AutoPtr(r, m) => {
+                        self.guarantee_valid(expr.id,
+                                             expr.span,
+                                             cmt,
+                                             m,
+                                             r)
+                    }
+                    ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
+                        let cmt_index = mcx.cat_index(expr, cmt);
+                        self.guarantee_valid(expr.id,
+                                             expr.span,
+                                             cmt_index,
+                                             m,
+                                             r)
+                    }
+                    ty::AutoBorrowFn(r) => {
+                        let cmt_deref = mcx.cat_deref_fn(expr, cmt, 0);
+                        self.guarantee_valid(expr.id,
+                                             expr.span,
+                                             cmt_deref,
+                                             m_imm,
+                                             r)
+                    }
+                    ty::AutoUnsafe(_) => {}
+                }
+            }
+        }
+    }
+
+    // Guarantees that addr_of(cmt) will be valid for the duration of
+    // `static_scope_r`, or reports an error.  This may entail taking
+    // out loans, which will be added to the `req_loan_map`.  This can
+    // also entail "rooting" GC'd pointers, which means ensuring
+    // dynamically that they are not freed.
+    fn guarantee_valid(&mut self,
+                       borrow_id: ast::node_id,
+                       borrow_span: span,
+                       cmt: mc::cmt,
+                       req_mutbl: ast::mutability,
+                       loan_region: ty::Region)
+    {
+        debug!("guarantee_valid(borrow_id=%?, cmt=%s, \
+                req_mutbl=%?, loan_region=%?)",
+               borrow_id,
+               cmt.repr(self.tcx()),
+               req_mutbl,
+               loan_region);
+
+        // a loan for the empty region can never be dereferenced, so
+        // it is always safe
+        if loan_region == ty::re_empty {
+            return;
+        }
+
+        let root_ub = { *self.repeating_ids.last() }; // FIXME(#5074)
+
+        // Check that the lifetime of the borrow does not exceed
+        // the lifetime of the data being borrowed.
+        lifetime::guarantee_lifetime(self.bccx, self.item_ub, root_ub,
+                                     borrow_span, cmt, loan_region, req_mutbl);
+
+        // Check that we don't allow mutable borrows of non-mutable data.
+        check_mutability(self.bccx, borrow_span, cmt, req_mutbl);
+
+        // Compute the restrictions that are required to enforce the
+        // loan is safe.
+        let restr = restrictions::compute_restrictions(
+            self.bccx, borrow_span,
+            cmt, self.restriction_set(req_mutbl));
+
+        // Create the loan record (if needed).
+        let loan = match restr {
+            restrictions::Safe => {
+                // No restrictions---no loan record necessary
+                return;
+            }
+
+            restrictions::SafeIf(loan_path, restrictions) => {
+                let loan_scope = match loan_region {
+                    ty::re_scope(id) => id,
+                    ty::re_free(ref fr) => fr.scope_id,
+
+                    ty::re_static => {
+                        // If we get here, an error must have been
+                        // reported in
+                        // `lifetime::guarantee_lifetime()`, because
+                        // the only legal ways to have a borrow with a
+                        // static lifetime should not require
+                        // restrictions. To avoid reporting derived
+                        // errors, we just return here without adding
+                        // any loans.
+                        return;
+                    }
+
+                    ty::re_empty |
+                    ty::re_bound(*) |
+                    ty::re_infer(*) => {
+                        self.tcx().sess.span_bug(
+                            cmt.span,
+                            fmt!("Invalid borrow lifetime: %?", loan_region));
+                    }
+                };
+                debug!("loan_scope = %?", loan_scope);
+
+                let gen_scope = self.compute_gen_scope(borrow_id, loan_scope);
+                debug!("gen_scope = %?", gen_scope);
+
+                let kill_scope = self.compute_kill_scope(loan_scope, loan_path);
+                debug!("kill_scope = %?", kill_scope);
+
+                if req_mutbl == m_mutbl {
+                    self.mark_loan_path_as_mutated(loan_path);
+                }
+
+                let all_loans = &mut *self.all_loans; // FIXME(#5074)
+                Loan {
+                    index: all_loans.len(),
+                    loan_path: loan_path,
+                    cmt: cmt,
+                    mutbl: req_mutbl,
+                    gen_scope: gen_scope,
+                    kill_scope: kill_scope,
+                    span: borrow_span,
+                    restrictions: restrictions
+                }
+            }
+        };
+
+        debug!("guarantee_valid(borrow_id=%?), loan=%s",
+               borrow_id, loan.repr(self.tcx()));
+
+        // let loan_path = loan.loan_path;
+        // let loan_gen_scope = loan.gen_scope;
+        // let loan_kill_scope = loan.kill_scope;
+        self.all_loans.push(loan);
+
+        // if loan_gen_scope != borrow_id {
+            // NOTE handle case where gen_scope is not borrow_id
+            //
+            // Typically, the scope of the loan includes the point at
+            // which the loan is originated. This
+            // This is a subtle case. See the test case
+            // <compile-fail/borrowck-bad-nested-calls-free.rs>
+            // to see what we are guarding against.
+
+            //let restr = restrictions::compute_restrictions(
+            //    self.bccx, borrow_span, cmt, RESTR_EMPTY);
+            //let loan = {
+            //    let all_loans = &mut *self.all_loans; // FIXME(#5074)
+            //    Loan {
+            //        index: all_loans.len(),
+            //        loan_path: loan_path,
+            //        cmt: cmt,
+            //        mutbl: m_const,
+            //        gen_scope: borrow_id,
+            //        kill_scope: kill_scope,
+            //        span: borrow_span,
+            //        restrictions: restrictions
+            //    }
+        // }
+
+        fn check_mutability(bccx: @BorrowckCtxt,
+                            borrow_span: span,
+                            cmt: mc::cmt,
+                            req_mutbl: ast::mutability) {
+            match req_mutbl {
+                m_const => {
+                    // Data of any mutability can be lent as const.
+                }
+
+                m_imm => {
+                    match cmt.mutbl {
+                        mc::McImmutable | mc::McDeclared | mc::McInherited => {
+                            // both imm and mut data can be lent as imm;
+                            // for mutable data, this is a freeze
+                        }
+                        mc::McReadOnly => {
+                            bccx.report(BckError {span: borrow_span,
+                                                  cmt: cmt,
+                                                  code: err_mutbl(req_mutbl)});
+                        }
+                    }
+                }
+
+                m_mutbl => {
+                    // Only mutable data can be lent as mutable.
+                    if !cmt.mutbl.is_mutable() {
+                        bccx.report(BckError {span: borrow_span,
+                                              cmt: cmt,
+                                              code: err_mutbl(req_mutbl)});
+                    }
+                }
+            }
+        }
+    }
+
+    fn restriction_set(&self, req_mutbl: ast::mutability) -> RestrictionSet {
+        match req_mutbl {
+            m_const => RESTR_EMPTY,
+            m_imm   => RESTR_EMPTY | RESTR_MUTATE,
+            m_mutbl => RESTR_EMPTY | RESTR_MUTATE | RESTR_FREEZE
+        }
+    }
+
+    fn mark_loan_path_as_mutated(&self, loan_path: @LoanPath) {
+        //! For mutable loans of content whose mutability derives
+        //! from a local variable, mark the mutability decl as necessary.
+
+        match *loan_path {
+            LpVar(local_id) => {
+                self.tcx().used_mut_nodes.insert(local_id);
+            }
+            LpExtend(base, mc::McInherited, _) => {
+                self.mark_loan_path_as_mutated(base);
+            }
+            LpExtend(_, mc::McDeclared, _) |
+            LpExtend(_, mc::McImmutable, _) |
+            LpExtend(_, mc::McReadOnly, _) => {
+            }
+        }
+    }
+
+    fn compute_gen_scope(&self,
+                         borrow_id: ast::node_id,
+                         loan_scope: ast::node_id) -> ast::node_id {
+        //! Determine when to introduce the loan. Typically the loan
+        //! is introduced at the point of the borrow, but in some cases,
+        //! notably method arguments, the loan may be introduced only
+        //! later, once it comes into scope.
+
+        let rm = self.bccx.tcx.region_maps;
+        if rm.is_subscope_of(borrow_id, loan_scope) {
+            borrow_id
+        } else {
+            loan_scope
+        }
+    }
+
+    fn compute_kill_scope(&self,
+                          loan_scope: ast::node_id,
+                          lp: @LoanPath) -> ast::node_id {
+        //! Determine when the loan restrictions go out of scope.
+        //! This is either when the lifetime expires or when the
+        //! local variable which roots the loan-path goes out of scope,
+        //! whichever happens faster.
+        //!
+        //! It may seem surprising that we might have a loan region
+        //! larger than the variable which roots the loan-path; this can
+        //! come about when variables of `&mut` type are re-borrowed,
+        //! as in this example:
+        //!
+        //!     fn counter<'a>(v: &'a mut Foo) -> &'a mut uint {
+        //!         &mut v.counter
+        //!     }
+        //!
+        //! In this case, the borrowed pointer (`'a`) outlives the
+        //! variable `v` that hosts it. Note that this doesn't come up
+        //! with immutable `&` pointers, because borrows of such pointers
+        //! do not require restrictions and hence do not cause a loan.
+
+        let rm = self.bccx.tcx.region_maps;
+        let lexical_scope = rm.encl_scope(lp.node_id());
+        if rm.is_subscope_of(lexical_scope, loan_scope) {
+            lexical_scope
+        } else {
+            assert!(rm.is_subscope_of(loan_scope, lexical_scope));
+            loan_scope
+        }
+    }
+
+    fn gather_pat(&mut self,
+                  discr_cmt: mc::cmt,
+                  root_pat: @ast::pat,
+                  arm_body_id: ast::node_id,
+                  match_id: ast::node_id) {
+        do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
+            match pat.node {
+              ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
+                match bm {
+                  ast::bind_by_ref(mutbl) => {
+                    // ref x or ref x @ p --- creates a ptr which must
+                    // remain valid for the scope of the match
+
+                    // find the region of the resulting pointer (note that
+                    // the type of such a pattern will *always* be a
+                    // region pointer)
+                    let scope_r =
+                        ty_region(self.tcx(), pat.span,
+                                  ty::node_id_to_type(self.tcx(), pat.id));
+
+                    // if the scope of the region ptr turns out to be
+                    // specific to this arm, wrap the categorization
+                    // with a cat_discr() node.  There is a detailed
+                    // discussion of the function of this node in
+                    // `lifetime.rs`:
+                    let arm_scope = ty::re_scope(arm_body_id);
+                    if self.bccx.is_subregion_of(scope_r, arm_scope) {
+                        let cmt_discr = self.bccx.cat_discr(cmt, match_id);
+                        self.guarantee_valid(pat.id, pat.span,
+                                             cmt_discr, mutbl, scope_r);
+                    } else {
+                        self.guarantee_valid(pat.id, pat.span,
+                                             cmt, mutbl, scope_r);
+                    }
+                  }
+                  ast::bind_by_copy | ast::bind_infer => {
+                    // Nothing to do here; neither copies nor moves induce
+                    // borrows.
+                  }
+                }
+              }
+
+              ast::pat_vec(_, Some(slice_pat), _) => {
+                  // The `slice_pat` here creates a slice into the
+                  // original vector.  This is effectively a borrow of
+                  // the elements of the vector being matched.
+
+                  let slice_ty = ty::node_id_to_type(self.tcx(),
+                                                     slice_pat.id);
+                  let (slice_mutbl, slice_r) =
+                      self.vec_slice_info(slice_pat, slice_ty);
+                  let mcx = self.bccx.mc_ctxt();
+                  let cmt_index = mcx.cat_index(slice_pat, cmt);
+                  self.guarantee_valid(pat.id, pat.span,
+                                       cmt_index, slice_mutbl, slice_r);
+              }
+
+              _ => {}
+            }
+        }
+    }
+
+    fn vec_slice_info(&self,
+                      pat: @ast::pat,
+                      slice_ty: ty::t) -> (ast::mutability, ty::Region) {
+        /*!
+         *
+         * In a pattern like [a, b, ..c], normally `c` has slice type,
+         * but if you have [a, b, ..ref c], then the type of `ref c`
+         * will be `&&[]`, so to extract the slice details we have
+         * to recurse through rptrs.
+         */
+
+        match ty::get(slice_ty).sty {
+            ty::ty_evec(slice_mt, ty::vstore_slice(slice_r)) => {
+                (slice_mt.mutbl, slice_r)
+            }
+
+            ty::ty_rptr(_, ref mt) => {
+                self.vec_slice_info(pat, mt.ty)
+            }
+
+            _ => {
+                self.tcx().sess.span_bug(
+                    pat.span,
+                    fmt!("Type of slice pattern is not a slice"));
+            }
+        }
+    }
+
+    fn pat_is_variant_or_struct(&self, pat: @ast::pat) -> bool {
+        pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat)
+    }
+
+    fn pat_is_binding(&self, pat: @ast::pat) -> bool {
+        pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)
+    }
+}
+
+// Setting up info that preserve needs.
+// This is just the most convenient place to do it.
+fn add_stmt_to_map(stmt: @ast::stmt,
+                   self: @mut GatherLoanCtxt,
+                   vt: visit::vt<@mut GatherLoanCtxt>) {
+    match stmt.node {
+        ast::stmt_expr(_, id) | ast::stmt_semi(_, id) => {
+            self.bccx.stmt_map.insert(id);
+        }
+        _ => ()
+    }
+    visit::visit_stmt(stmt, self, vt);
+}
diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs
new file mode 100644
index 00000000000..950dbc58ec3
--- /dev/null
+++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs
@@ -0,0 +1,251 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Computes the restrictions that result from a borrow.
+
+use core::prelude::*;
+use middle::borrowck::*;
+use mc = middle::mem_categorization;
+use middle::ty;
+use syntax::ast::{m_const, m_imm, m_mutbl};
+use syntax::ast;
+use syntax::codemap::span;
+
+pub enum RestrictionResult {
+    Safe,
+    SafeIf(@LoanPath, ~[Restriction])
+}
+
+pub fn compute_restrictions(bccx: @BorrowckCtxt,
+                            span: span,
+                            cmt: mc::cmt,
+                            restr: RestrictionSet) -> RestrictionResult {
+    let ctxt = RestrictionsContext {
+        bccx: bccx,
+        span: span,
+        cmt_original: cmt
+    };
+
+    ctxt.compute(cmt, restr)
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Private
+
+struct RestrictionsContext {
+    bccx: @BorrowckCtxt,
+    span: span,
+    cmt_original: mc::cmt
+}
+
+impl RestrictionsContext {
+    fn tcx(&self) -> ty::ctxt {
+        self.bccx.tcx
+    }
+
+    fn compute(&self,
+               cmt: mc::cmt,
+               restrictions: RestrictionSet) -> RestrictionResult {
+
+        // Check for those cases where we cannot control the aliasing
+        // and make sure that we are not being asked to.
+        match cmt.freely_aliasable() {
+            None => {}
+            Some(cause) => {
+                self.check_aliasing_permitted(cause, restrictions);
+            }
+        }
+
+        match cmt.cat {
+            mc::cat_rvalue => {
+                // Effectively, rvalues are stored into a
+                // non-aliasable temporary on the stack. Since they
+                // are inherently non-aliasable, they can only be
+                // accessed later through the borrow itself and hence
+                // must inherently comply with its terms.
+                Safe
+            }
+
+            mc::cat_local(local_id) |
+            mc::cat_arg(local_id, ast::by_copy) |
+            mc::cat_self(local_id) => {
+                let lp = @LpVar(local_id);
+                SafeIf(lp, ~[Restriction {loan_path: lp,
+                                          set: restrictions}])
+            }
+
+            mc::cat_interior(cmt_base, i @ mc::interior_variant(_)) => {
+                // When we borrow the interior of an enum, we have to
+                // ensure the enum itself is not mutated, because that
+                // could cause the type of the memory to change.
+                let result = self.compute(cmt_base, restrictions | RESTR_MUTATE);
+                self.extend(result, cmt.mutbl, LpInterior(i), restrictions)
+            }
+
+            mc::cat_interior(cmt_base, i @ mc::interior_tuple) |
+            mc::cat_interior(cmt_base, i @ mc::interior_anon_field) |
+            mc::cat_interior(cmt_base, i @ mc::interior_field(*)) |
+            mc::cat_interior(cmt_base, i @ mc::interior_index(*)) => {
+                // For all of these cases, overwriting the base would
+                // not change the type of the memory, so no additional
+                // restrictions are needed.
+                //
+                // FIXME(#5397) --- Mut fields are not treated soundly
+                //                  (hopefully they will just get phased out)
+                let result = self.compute(cmt_base, restrictions);
+                self.extend(result, cmt.mutbl, LpInterior(i), restrictions)
+            }
+
+            mc::cat_deref(cmt_base, _, mc::uniq_ptr(*)) => {
+                // When we borrow the interior of an owned pointer, we
+                // cannot permit the base to be mutated, because that
+                // would cause the unique pointer to be freed.
+                let result = self.compute(cmt_base, restrictions | RESTR_MUTATE);
+                self.extend(result, cmt.mutbl, LpDeref, restrictions)
+            }
+
+            mc::cat_copied_upvar(*) | // FIXME(#2152) allow mutation of upvars
+            mc::cat_static_item(*) |
+            mc::cat_implicit_self(*) |
+            mc::cat_arg(_, ast::by_ref) |
+            mc::cat_deref(_, _, mc::region_ptr(m_imm, _)) |
+            mc::cat_deref(_, _, mc::gc_ptr(m_imm)) => {
+                Safe
+            }
+
+            mc::cat_deref(_, _, mc::region_ptr(m_const, _)) |
+            mc::cat_deref(_, _, mc::gc_ptr(m_const)) => {
+                self.check_no_mutability_control(cmt, restrictions);
+                Safe
+            }
+
+            mc::cat_deref(cmt_base, _, mc::gc_ptr(m_mutbl)) => {
+                // Technically, no restrictions are *necessary* here.
+                // The validity of the borrow is guaranteed
+                // dynamically.  However, nonetheless we add a
+                // restriction to make a "best effort" to report
+                // static errors. For example, if there is code like
+                //
+                //    let v = @mut ~[1, 2, 3];
+                //    for v.each |e| {
+                //        v.push(e + 1);
+                //    }
+                //
+                // Then the code below would add restrictions on `*v`,
+                // which means that an error would be reported
+                // here. This of course is not perfect. For example,
+                // a function like the following would not report an error
+                // at compile-time but would fail dynamically:
+                //
+                //    let v = @mut ~[1, 2, 3];
+                //    let w = v;
+                //    for v.each |e| {
+                //        w.push(e + 1);
+                //    }
+                //
+                // In addition, we only add a restriction for those cases
+                // where we can construct a sensible loan path, so an
+                // example like the following will fail dynamically:
+                //
+                //    impl V {
+                //      fn get_list(&self) -> @mut ~[int];
+                //    }
+                //    ...
+                //    let v: &V = ...;
+                //    for v.get_list().each |e| {
+                //        v.get_list().push(e + 1);
+                //    }
+                match opt_loan_path(cmt_base) {
+                    None => Safe,
+                    Some(lp_base) => {
+                        let lp = @LpExtend(lp_base, cmt.mutbl, LpDeref);
+                        SafeIf(lp, ~[Restriction {loan_path: lp,
+                                                  set: restrictions}])
+                    }
+                }
+            }
+
+            mc::cat_deref(cmt_base, _, mc::region_ptr(m_mutbl, _)) => {
+                // Because an `&mut` pointer does not inherit its
+                // mutability, we can only prevent mutation or prevent
+                // freezing if it is not aliased. Therefore, in such
+                // cases we restrict aliasing on `cmt_base`.
+                if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE) {
+                    let result = self.compute(cmt_base, restrictions | RESTR_ALIAS);
+                    self.extend(result, cmt.mutbl, LpDeref, restrictions)
+                } else {
+                    let result = self.compute(cmt_base, restrictions);
+                    self.extend(result, cmt.mutbl, LpDeref, restrictions)
+                }
+            }
+
+            mc::cat_deref(_, _, mc::unsafe_ptr) => {
+                // We are very trusting when working with unsafe pointers.
+                Safe
+            }
+
+            mc::cat_stack_upvar(cmt_base) |
+            mc::cat_discr(cmt_base, _) => {
+                self.compute(cmt_base, restrictions)
+            }
+        }
+    }
+
+    fn extend(&self,
+              result: RestrictionResult,
+              mc: mc::MutabilityCategory,
+              elem: LoanPathElem,
+              restrictions: RestrictionSet) -> RestrictionResult {
+        match result {
+            Safe => Safe,
+            SafeIf(base_lp, base_vec) => {
+                let lp = @LpExtend(base_lp, mc, elem);
+                SafeIf(lp, vec::append_one(base_vec,
+                                           Restriction {loan_path: lp,
+                                                        set: restrictions}))
+            }
+        }
+    }
+
+    fn check_aliasing_permitted(&self,
+                                cause: mc::AliasableReason,
+                                restrictions: RestrictionSet) {
+        //! This method is invoked when the current `cmt` is something
+        //! where aliasing cannot be controlled. It reports an error if
+        //! the restrictions required that it not be aliased; currently
+        //! this only occurs when re-borrowing an `&mut` pointer.
+        //!
+        //! NB: To be 100% consistent, we should report an error if
+        //! RESTR_FREEZE is found, because we cannot prevent freezing,
+        //! nor would we want to. However, we do not report such an
+        //! error, because this restriction only occurs when the user
+        //! is creating an `&mut` pointer to immutable or read-only
+        //! data, and there is already another piece of code that
+        //! checks for this condition.
+
+        if restrictions.intersects(RESTR_ALIAS) {
+            self.bccx.report_aliasability_violation(
+                self.span,
+                BorrowViolation,
+                cause);
+        }
+    }
+
+    fn check_no_mutability_control(&self,
+                                   cmt: mc::cmt,
+                                   restrictions: RestrictionSet) {
+        if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE) {
+            self.bccx.report(BckError {span: self.span,
+                                       cmt: cmt,
+                                       code: err_freeze_aliasable_const});
+        }
+    }
+}
+
diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs
deleted file mode 100644
index 21de29b8f60..00000000000
--- a/src/librustc/middle/borrowck/loan.rs
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-/*!
-
-The `Loan` module deals with borrows of *uniquely mutable* data.  We
-say that data is uniquely mutable if the current activation (stack
-frame) controls the only mutable reference to the data.  The most
-common way that this can occur is if the current activation owns the
-data being borrowed, but it can also occur with `&mut` pointers.  The
-primary characteristic of uniquely mutable data is that, at any given
-time, there is at most one path that can be used to mutate it, and
-that path is only accessible from the top stack frame.
-
-Given that some data found at a path P is being borrowed to a borrowed
-pointer with mutability M and lifetime L, the job of the code in this
-module is to compute the set of *loans* that are necessary to ensure
-that (1) the data found at P outlives L and that (2) if M is mutable
-then the path P will not be modified directly or indirectly except
-through that pointer.  A *loan* is the combination of a path P_L, a
-mutability M_L, and a lifetime L_L where:
-
-- The path P_L indicates what data has been lent.
-- The mutability M_L indicates the access rights on the data:
-  - const: the data cannot be moved
-  - immutable/mutable: the data cannot be moved or mutated
-- The lifetime L_L indicates the *scope* of the loan.
-
-FIXME #4730 --- much more needed, don't have time to write this all up now
-
-*/
-
-// ----------------------------------------------------------------------
-// Loan(Ex, M, S) = Ls holds if ToAddr(Ex) will remain valid for the entirety
-// of the scope S, presuming that the returned set of loans `Ls` are honored.
-
-use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
-use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze,
-                       TotalTake, PartialTake, Immobile};
-use middle::borrowck::{err_out_of_scope};
-use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp};
-use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self};
-use middle::mem_categorization::{cat_special, cat_stack_upvar, cmt};
-use middle::mem_categorization::{comp_field, comp_index, comp_variant};
-use middle::mem_categorization::{gc_ptr, region_ptr};
-use middle::ty;
-use util::common::indenter;
-
-use syntax::ast::m_imm;
-use syntax::ast;
-
-pub fn loan(bccx: @BorrowckCtxt,
-            cmt: cmt,
-            scope_region: ty::Region,
-            loan_kind: LoanKind) -> bckres<~[Loan]>
-{
-    let mut lc = LoanContext {
-        bccx: bccx,
-        scope_region: scope_region,
-        loans: ~[]
-    };
-    match lc.loan(cmt, loan_kind, true) {
-        Err(ref e) => return Err((*e)),
-        Ok(()) => {}
-    }
-    // FIXME #4945: Workaround for borrow check bug.
-    Ok(copy lc.loans)
-}
-
-struct LoanContext {
-    bccx: @BorrowckCtxt,
-
-    // the region scope for which we must preserve the memory
-    scope_region: ty::Region,
-
-    // accumulated list of loans that will be required
-    loans: ~[Loan]
-}
-
-pub impl LoanContext {
-    fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
-
-    fn loan(&mut self,
-            cmt: cmt,
-            loan_kind: LoanKind,
-            owns_lent_data: bool) -> bckres<()>
-    {
-        /*!
-         *
-         * The main routine.
-         *
-         * # Parameters
-         *
-         * - `cmt`: the categorization of the data being borrowed
-         * - `req_mutbl`: the mutability of the borrowed pointer
-         *                that was created
-         * - `owns_lent_data`: indicates whether `cmt` owns the
-         *                     data that is being lent.  See
-         *                     discussion in `issue_loan()`.
-         */
-
-        debug!("loan(%s, %?)",
-               self.bccx.cmt_to_repr(cmt),
-               loan_kind);
-        let _i = indenter();
-
-        // see stable() above; should only be called when `cmt` is lendable
-        if cmt.lp.is_none() {
-            self.bccx.tcx.sess.span_bug(
-                cmt.span,
-                ~"loan() called with non-lendable value");
-        }
-
-        match cmt.cat {
-          cat_binding(_) | cat_rvalue | cat_special(_) => {
-            // should never be loanable
-            self.bccx.tcx.sess.span_bug(
-                cmt.span,
-                ~"rvalue with a non-none lp");
-          }
-          cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => {
-              // FIXME(#4903)
-            let local_region = self.bccx.tcx.region_maps.encl_region(local_id);
-            self.issue_loan(cmt, local_region, loan_kind,
-                            owns_lent_data)
-          }
-          cat_stack_upvar(cmt) => {
-            self.loan(cmt, loan_kind, owns_lent_data)
-          }
-          cat_discr(base, _) => {
-            self.loan(base, loan_kind, owns_lent_data)
-          }
-          cat_comp(cmt_base, comp_field(_, m)) |
-          cat_comp(cmt_base, comp_index(_, m)) => {
-            // For most components, the type of the embedded data is
-            // stable.  Therefore, the base structure need only be
-            // const---unless the component must be immutable.  In
-            // that case, it must also be embedded in an immutable
-            // location, or else the whole structure could be
-            // overwritten and the component along with it.
-            self.loan_stable_comp(cmt, cmt_base, loan_kind, m,
-                                  owns_lent_data)
-          }
-          cat_comp(cmt_base, comp_tuple) |
-          cat_comp(cmt_base, comp_anon_field) => {
-            // As above.
-            self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm,
-                                  owns_lent_data)
-          }
-          cat_comp(cmt_base, comp_variant(enum_did)) => {
-            // For enums, the memory is unstable if there are multiple
-            // variants, because if the enum value is overwritten then
-            // the memory changes type.
-            if ty::enum_is_univariant(self.bccx.tcx, enum_did) {
-                self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm,
-                                      owns_lent_data)
-            } else {
-                self.loan_unstable_deref(cmt, cmt_base, loan_kind,
-                                         owns_lent_data)
-            }
-          }
-          cat_deref(cmt_base, _, uniq_ptr) => {
-            // For unique pointers, the memory being pointed out is
-            // unstable because if the unique pointer is overwritten
-            // then the memory is freed.
-            self.loan_unstable_deref(cmt, cmt_base, loan_kind,
-                                     owns_lent_data)
-          }
-          cat_deref(cmt_base, _, region_ptr(ast::m_mutbl, region)) => {
-            // Mutable data can be loaned out as immutable or const. We must
-            // loan out the base as well as the main memory. For example,
-            // if someone borrows `*b`, we want to borrow `b` as immutable
-            // as well.
-            do self.loan(cmt_base, TotalFreeze, false).chain |_| {
-                self.issue_loan(cmt, region, loan_kind, owns_lent_data)
-            }
-          }
-          cat_deref(_, _, unsafe_ptr) |
-          cat_deref(_, _, gc_ptr(_)) |
-          cat_deref(_, _, region_ptr(_, _)) => {
-            // Aliased data is simply not lendable.
-            self.bccx.tcx.sess.span_bug(
-                cmt.span,
-                ~"aliased ptr with a non-none lp");
-          }
-        }
-    }
-
-    // A "stable component" is one where assigning the base of the
-    // component cannot cause the component itself to change types.
-    // Example: record fields.
-    fn loan_stable_comp(&mut self,
-                        cmt: cmt,
-                        cmt_base: cmt,
-                        loan_kind: LoanKind,
-                        comp_mutbl: ast::mutability,
-                        owns_lent_data: bool) -> bckres<()>
-    {
-        let base_kind = match (comp_mutbl, loan_kind) {
-            // Declared as "immutable" means: inherited mutability and
-            // hence mutable iff parent is mutable.  So propagate
-            // mutability on up.
-            (m_imm, TotalFreeze) | (m_imm, PartialFreeze) => PartialFreeze,
-            (m_imm, TotalTake) | (m_imm, PartialTake) => PartialTake,
-
-            // Declared as "mutable" means: always mutable no matter
-            // what the mutability of the base is.  So that means we
-            // can weaken the condition on the base to PartialFreeze.
-            // This implies that the user could freeze the base, but
-            // that is ok since the even with an &T base, the mut
-            // field will still be considered mutable.
-            (_, TotalTake) | (_, PartialTake) |
-            (_, TotalFreeze) | (_, PartialFreeze) => {
-                PartialFreeze
-            }
-
-            // If we just need to guarantee the value won't be moved,
-            // it doesn't matter what mutability the component was
-            // declared with.
-            (_, Immobile) => Immobile,
-        };
-
-        do self.loan(cmt_base, base_kind, owns_lent_data).chain |_ok| {
-            // can use static for the scope because the base
-            // determines the lifetime, ultimately
-            self.issue_loan(cmt, ty::re_static, loan_kind,
-                            owns_lent_data)
-        }
-    }
-
-    // An "unstable deref" means a deref of a ptr/comp where, if the
-    // base of the deref is assigned to, pointers into the result of the
-    // deref would be invalidated. Examples: interior of variants, uniques.
-    fn loan_unstable_deref(&mut self,
-                           cmt: cmt,
-                           cmt_base: cmt,
-                           loan_kind: LoanKind,
-                           owns_lent_data: bool) -> bckres<()> {
-        // Variant components: the base must be immutable, because
-        // if it is overwritten, the types of the embedded data
-        // could change.
-        do self.loan(cmt_base, PartialFreeze, owns_lent_data).chain |_| {
-            // can use static, as in loan_stable_comp()
-            self.issue_loan(cmt, ty::re_static, loan_kind,
-                            owns_lent_data)
-        }
-    }
-
-    fn issue_loan(&mut self,
-                  cmt: cmt,
-                  scope_ub: ty::Region,
-                  loan_kind: LoanKind,
-                  owns_lent_data: bool) -> bckres<()> {
-        // Subtle: the `scope_ub` is the maximal lifetime of `cmt`.
-        // Therefore, if `cmt` owns the data being lent, then the
-        // scope of the loan must be less than `scope_ub`, or else the
-        // data would be freed while the loan is active.
-        //
-        // However, if `cmt` does *not* own the data being lent, then
-        // it is ok if `cmt` goes out of scope during the loan.  This
-        // can occur when you have an `&mut` parameter that is being
-        // reborrowed.
-
-        if !owns_lent_data ||
-            self.bccx.is_subregion_of(self.scope_region, scope_ub)
-        {
-            if cmt.mutbl.is_mutable() {
-                // If this loan is a mutable loan, then mark the loan path (if
-                // it exists) as being used. This is similar to the check
-                // performed in check_loans.rs in check_assignment(), but this
-                // is for a different purpose of having the 'mut' qualifier.
-                for cmt.lp.each |lp| {
-                    for lp.node_id().each |&id| {
-                        self.tcx().used_mut_nodes.insert(id);
-                    }
-                }
-            } else if loan_kind.is_take() {
-                // We do not allow non-mutable data to be "taken"
-                // under any circumstances.
-                return Err(bckerr {
-                    cmt:cmt,
-                    code:err_mutbl(loan_kind)
-                });
-            }
-
-            self.loans.push(Loan {
-                // Note: cmt.lp must be Some(_) because otherwise this
-                // loan process does not apply at all.
-                lp: cmt.lp.get(),
-                cmt: cmt,
-                kind: loan_kind
-            });
-
-            return Ok(());
-        } else {
-            // The loan being requested lives longer than the data
-            // being loaned out!
-            return Err(bckerr {
-                cmt:cmt,
-                code:err_out_of_scope(scope_ub, self.scope_region)
-            });
-        }
-    }
-}
-
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index 3746f9c6e60..c108b020378 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -8,254 +8,64 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-/*!
-# Borrow check
-
-This pass is in job of enforcing *memory safety* and *purity*.  As
-memory safety is by far the more complex topic, I'll focus on that in
-this description, but purity will be covered later on. In the context
-of Rust, memory safety means three basic things:
-
-- no writes to immutable memory;
-- all pointers point to non-freed memory;
-- all pointers point to memory of the same type as the pointer.
-
-The last point might seem confusing: after all, for the most part,
-this condition is guaranteed by the type check.  However, there are
-two cases where the type check effectively delegates to borrow check.
-
-The first case has to do with enums.  If there is a pointer to the
-interior of an enum, and the enum is in a mutable location (such as a
-local variable or field declared to be mutable), it is possible that
-the user will overwrite the enum with a new value of a different
-variant, and thus effectively change the type of the memory that the
-pointer is pointing at.
+/*! See doc.rs for a thorough explanation of the borrow checker */
 
-The second case has to do with mutability.  Basically, the type
-checker has only a limited understanding of mutability.  It will allow
-(for example) the user to get an immutable pointer with the address of
-a mutable local variable.  It will also allow a `@mut T` or `~mut T`
-pointer to be borrowed as a `&r.T` pointer.  These seeming oversights
-are in fact intentional; they allow the user to temporarily treat a
-mutable value as immutable.  It is up to the borrow check to guarantee
-that the value in question is not in fact mutated during the lifetime
-`r` of the reference.
+use core;
+use core::prelude::*;
 
-# Definition of unstable memory
-
-The primary danger to safety arises due to *unstable memory*.
-Unstable memory is memory whose validity or type may change as a
-result of an assignment, move, or a variable going out of scope.
-There are two cases in Rust where memory is unstable: the contents of
-unique boxes and enums.
-
-Unique boxes are unstable because when the variable containing the
-unique box is re-assigned, moves, or goes out of scope, the unique box
-is freed or---in the case of a move---potentially given to another
-task.  In either case, if there is an extant and usable pointer into
-the box, then safety guarantees would be compromised.
-
-Enum values are unstable because they are reassigned the types of
-their contents may change if they are assigned with a different
-variant than they had previously.
-
-# Safety criteria that must be enforced
-
-Whenever a piece of memory is borrowed for lifetime L, there are two
-things which the borrow checker must guarantee.  First, it must
-guarantee that the memory address will remain allocated (and owned by
-the current task) for the entirety of the lifetime L.  Second, it must
-guarantee that the type of the data will not change for the entirety
-of the lifetime L.  In exchange, the region-based type system will
-guarantee that the pointer is not used outside the lifetime L.  These
-guarantees are to some extent independent but are also inter-related.
-
-In some cases, the type of a pointer cannot be invalidated but the
-lifetime can.  For example, imagine a pointer to the interior of
-a shared box like:
-
-    let mut x = @mut {f: 5, g: 6};
-    let y = &mut x.f;
-
-Here, a pointer was created to the interior of a shared box which
-contains a record.  Even if `*x` were to be mutated like so:
-
-    *x = {f: 6, g: 7};
-
-This would cause `*y` to change from 5 to 6, but the pointer pointer
-`y` remains valid.  It still points at an integer even if that integer
-has been overwritten.
-
-However, if we were to reassign `x` itself, like so:
-
-    x = @{f: 6, g: 7};
-
-This could potentially invalidate `y`, because if `x` were the final
-reference to the shared box, then that memory would be released and
-now `y` points at freed memory.  (We will see that to prevent this
-scenario we will *root* shared boxes that reside in mutable memory
-whose contents are borrowed; rooting means that we create a temporary
-to ensure that the box is not collected).
-
-In other cases, like an enum on the stack, the memory cannot be freed
-but its type can change:
-
-    let mut x = Some(5);
-    match x {
-      Some(ref y) => { ... }
-      None => { ... }
-    }
-
-Here as before, the pointer `y` would be invalidated if we were to
-reassign `x` to `none`.  (We will see that this case is prevented
-because borrowck tracks data which resides on the stack and prevents
-variables from reassigned if there may be pointers to their interior)
-
-Finally, in some cases, both dangers can arise.  For example, something
-like the following:
-
-    let mut x = ~Some(5);
-    match x {
-      ~Some(ref y) => { ... }
-      ~None => { ... }
-    }
-
-In this case, if `x` to be reassigned or `*x` were to be mutated, then
-the pointer `y` would be invalided.  (This case is also prevented by
-borrowck tracking data which is owned by the current stack frame)
-
-# Summary of the safety check
-
-In order to enforce mutability, the borrow check has a few tricks up
-its sleeve:
-
-- When data is owned by the current stack frame, we can identify every
-  possible assignment to a local variable and simply prevent
-  potentially dangerous assignments directly.
-
-- If data is owned by a shared box, we can root the box to increase
-  its lifetime.
-
-- If data is found within a borrowed pointer, we can assume that the
-  data will remain live for the entirety of the borrowed pointer.
-
-- We can rely on the fact that pure actions (such as calling pure
-  functions) do not mutate data which is not owned by the current
-  stack frame.
-
-# Possible future directions
-
-There are numerous ways that the `borrowck` could be strengthened, but
-these are the two most likely:
-
-- flow-sensitivity: we do not currently consider flow at all but only
-  block-scoping.  This means that innocent code like the following is
-  rejected:
-
-      let mut x: int;
-      ...
-      x = 5;
-      let y: &int = &x; // immutable ptr created
-      ...
-
-  The reason is that the scope of the pointer `y` is the entire
-  enclosing block, and the assignment `x = 5` occurs within that
-  block.  The analysis is not smart enough to see that `x = 5` always
-  happens before the immutable pointer is created.  This is relatively
-  easy to fix and will surely be fixed at some point.
-
-- finer-grained purity checks: currently, our fallback for
-  guaranteeing random references into mutable, aliasable memory is to
-  require *total purity*.  This is rather strong.  We could use local
-  type-based alias analysis to distinguish writes that could not
-  possibly invalid the references which must be guaranteed.  This
-  would only work within the function boundaries; function calls would
-  still require total purity.  This seems less likely to be
-  implemented in the short term as it would make the code
-  significantly more complex; there is currently no code to analyze
-  the types and determine the possible impacts of a write.
-
-# How the code works
-
-The borrow check code is divided into several major modules, each of
-which is documented in its own file.
-
-The `gather_loans` and `check_loans` are the two major passes of the
-analysis.  The `gather_loans` pass runs over the IR once to determine
-what memory must remain valid and for how long.  Its name is a bit of
-a misnomer; it does in fact gather up the set of loans which are
-granted, but it also determines when @T pointers must be rooted and
-for which scopes purity must be required.
-
-The `check_loans` pass walks the IR and examines the loans and purity
-requirements computed in `gather_loans`.  It checks to ensure that (a)
-the conditions of all loans are honored; (b) no contradictory loans
-were granted (for example, loaning out the same memory as mutable and
-immutable simultaneously); and (c) any purity requirements are
-honored.
-
-The remaining modules are helper modules used by `gather_loans` and
-`check_loans`:
-
-- `categorization` has the job of analyzing an expression to determine
-  what kind of memory is used in evaluating it (for example, where
-  dereferences occur and what kind of pointer is dereferenced; whether
-  the memory is mutable; etc)
-- `loan` determines when data uniquely tied to the stack frame can be
-  loaned out.
-- `preserve` determines what actions (if any) must be taken to preserve
-  aliasable data.  This is the code which decides when to root
-  an @T pointer or to require purity.
-
-# Maps that are created
-
-Borrowck results in two maps.
-
-- `root_map`: identifies those expressions or patterns whose result
-  needs to be rooted.  Conceptually the root_map maps from an
-  expression or pattern node to a `node_id` identifying the scope for
-  which the expression must be rooted (this `node_id` should identify
-  a block or call).  The actual key to the map is not an expression id,
-  however, but a `root_map_key`, which combines an expression id with a
-  deref count and is used to cope with auto-deref.
-
-- `mutbl_map`: identifies those local variables which are modified or
-  moved. This is used by trans to guarantee that such variables are
-  given a memory location and not used as immediates.
- */
-
-use middle::mem_categorization::*;
+use mc = middle::mem_categorization;
 use middle::ty;
 use middle::typeck;
 use middle::moves;
+use middle::dataflow::DataFlowContext;
+use middle::dataflow::DataFlowOperator;
 use util::common::stmt_set;
-use util::ppaux::note_and_explain_region;
+use util::ppaux::{note_and_explain_region, Repr};
 
 use core::hashmap::{HashSet, HashMap};
-use core::to_bytes;
-use syntax::ast::{mutability, m_imm};
+use core::io;
+use core::result::{Result};
+use core::ops::{BitOr, BitAnd};
 use syntax::ast;
+use syntax::ast_map;
+use syntax::visit;
 use syntax::codemap::span;
 
+macro_rules! if_ok(
+    ($inp: expr) => (
+        match $inp {
+            Ok(v) => { v }
+            Err(e) => { return Err(e); }
+        }
+    )
+)
+
+pub mod doc;
+
 pub mod check_loans;
+
+#[path="gather_loans/mod.rs"]
 pub mod gather_loans;
-pub mod loan;
-pub mod preserve;
+
+pub struct LoanDataFlowOperator;
+pub type LoanDataFlow = DataFlowContext<LoanDataFlowOperator>;
 
 pub fn check_crate(
     tcx: ty::ctxt,
     method_map: typeck::method_map,
     moves_map: moves::MovesMap,
+    moved_variables_set: moves::MovedVariablesSet,
     capture_map: moves::CaptureMap,
-    crate: @ast::crate) -> (root_map, mutbl_map, write_guard_map)
+    crate: @ast::crate) -> (root_map, write_guard_map)
 {
     let bccx = @BorrowckCtxt {
         tcx: tcx,
         method_map: method_map,
         moves_map: moves_map,
+        moved_variables_set: moved_variables_set,
         capture_map: capture_map,
         root_map: root_map(),
-        mutbl_map: @mut HashSet::new(),
+        loan_map: @mut HashMap::new(),
         write_guard_map: @mut HashSet::new(),
         stmt_map: @mut HashSet::new(),
         stats: @mut BorrowStats {
@@ -267,8 +77,9 @@ pub fn check_crate(
         }
     };
 
-    let req_maps = gather_loans::gather_loans(bccx, crate);
-    check_loans::check_loans(bccx, req_maps, crate);
+    let v = visit::mk_vt(@visit::Visitor {visit_fn: borrowck_fn,
+                                          ..*visit::default_visitor()});
+    visit::visit_crate(crate, bccx, v);
 
     if tcx.sess.borrowck_stats() {
         io::println(~"--- borrowck stats ---");
@@ -284,7 +95,7 @@ pub fn check_crate(
                          make_stat(bccx, bccx.stats.req_pure_paths)));
     }
 
-    return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map);
+    return (bccx.root_map, bccx.write_guard_map);
 
     fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> ~str {
         let stat_f = stat as float;
@@ -293,6 +104,46 @@ pub fn check_crate(
     }
 }
 
+fn borrowck_fn(fk: &visit::fn_kind,
+               decl: &ast::fn_decl,
+               body: &ast::blk,
+               sp: span,
+               id: ast::node_id,
+               self: @BorrowckCtxt,
+               v: visit::vt<@BorrowckCtxt>) {
+    match fk {
+        &visit::fk_anon(*) |
+        &visit::fk_fn_block(*) => {
+            // Closures are checked as part of their containing fn item.
+        }
+
+        &visit::fk_item_fn(*) |
+        &visit::fk_method(*) |
+        &visit::fk_dtor(*) => {
+            debug!("borrowck_fn(id=%?)", id);
+
+            // Check the body of fn items.
+            let (id_range, all_loans) =
+                gather_loans::gather_loans(self, body);
+            let all_loans: &~[Loan] = &*all_loans; // FIXME(#5074)
+            let mut dfcx =
+                DataFlowContext::new(self.tcx,
+                                     self.method_map,
+                                     LoanDataFlowOperator,
+                                     id_range,
+                                     all_loans.len());
+            for all_loans.eachi |loan_idx, loan| {
+                dfcx.add_gen(loan.gen_scope, loan_idx);
+                dfcx.add_kill(loan.kill_scope, loan_idx);
+            }
+            dfcx.propagate(body);
+            check_loans::check_loans(self, &dfcx, *all_loans, body);
+        }
+    }
+
+    visit::visit_fn(fk, decl, body, sp, id, self, v);
+}
+
 // ----------------------------------------------------------------------
 // Type definitions
 
@@ -300,9 +151,10 @@ pub struct BorrowckCtxt {
     tcx: ty::ctxt,
     method_map: typeck::method_map,
     moves_map: moves::MovesMap,
+    moved_variables_set: moves::MovedVariablesSet,
     capture_map: moves::CaptureMap,
     root_map: root_map,
-    mutbl_map: mutbl_map,
+    loan_map: LoanMap,
     write_guard_map: write_guard_map,
     stmt_map: stmt_set,
 
@@ -318,137 +170,228 @@ pub struct BorrowStats {
     guaranteed_paths: uint
 }
 
-pub struct RootInfo {
-    scope: ast::node_id,
-    // This will be true if we need to freeze this box at runtime. This will
-    // result in a call to `borrow_as_imm()` and `return_to_mut()`.
-    freezes: bool   // True if we need to freeze this box at runtime.
-}
-
-// a map mapping id's of expressions of gc'd type (@T, @[], etc) where
-// the box needs to be kept live to the id of the scope for which they
-// must stay live.
-pub type root_map = @mut HashMap<root_map_key, RootInfo>;
+pub type LoanMap = @mut HashMap<ast::node_id, @Loan>;
 
 // the keys to the root map combine the `id` of the expression with
-// the number of types that it is autodereferenced.  So, for example,
+// the number of types that it is autodereferenced. So, for example,
 // if you have an expression `x.f` and x has type ~@T, we could add an
 // entry {id:x, derefs:0} to refer to `x` itself, `{id:x, derefs:1}`
 // to refer to the deref of the unique pointer, and so on.
-#[deriving(Eq)]
+#[deriving(Eq, IterBytes)]
 pub struct root_map_key {
     id: ast::node_id,
     derefs: uint
 }
 
-// set of ids of local vars / formal arguments that are modified / moved.
-// this is used in trans for optimization purposes.
-pub type mutbl_map = @mut HashSet<ast::node_id>;
-
 // A set containing IDs of expressions of gc'd type that need to have a write
 // guard.
 pub type write_guard_map = @mut HashSet<root_map_key>;
 
-// Errors that can occur
-#[deriving(Eq)]
-pub enum bckerr_code {
-    err_mut_uniq,
-    err_mut_variant,
-    err_root_not_permitted,
-    err_mutbl(LoanKind),
-    err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
-    err_out_of_scope(ty::Region, ty::Region) // superscope, subscope
-}
+pub type BckResult<T> = Result<T, BckError>;
 
-// Combination of an error code and the categorization of the expression
-// that caused it
 #[deriving(Eq)]
-pub struct bckerr {
-    cmt: cmt,
-    code: bckerr_code
+pub enum PartialTotal {
+    Partial,   // Loan affects some portion
+    Total      // Loan affects entire path
 }
 
-pub enum MoveError {
-    MoveOk,
-    MoveFromIllegalCmt(cmt),
-    MoveWhileBorrowed(/*move*/ cmt, /*loan*/ cmt)
+///////////////////////////////////////////////////////////////////////////
+// Loans and loan paths
+
+/// Record of a loan that was issued.
+pub struct Loan {
+    index: uint,
+    loan_path: @LoanPath,
+    cmt: mc::cmt,
+    mutbl: ast::mutability,
+    restrictions: ~[Restriction],
+    gen_scope: ast::node_id,
+    kill_scope: ast::node_id,
+    span: span,
 }
 
-// shorthand for something that fails with `bckerr` or succeeds with `T`
-pub type bckres<T> = Result<T, bckerr>;
+#[deriving(Eq)]
+pub enum LoanPath {
+    LpVar(ast::node_id),               // `x` in doc.rs
+    LpExtend(@LoanPath, mc::MutabilityCategory, LoanPathElem)
+}
 
 #[deriving(Eq)]
-pub enum LoanKind {
-    TotalFreeze,   // Entire path is frozen   (borrowed as &T)
-    PartialFreeze, // Some subpath is frozen  (borrowed as &T)
-    TotalTake,     // Entire path is "taken"  (borrowed as &mut T)
-    PartialTake,   // Some subpath is "taken" (borrowed as &mut T)
-    Immobile       // Path cannot be moved    (borrowed as &const T)
+pub enum LoanPathElem {
+    LpDeref,                      // `*LV` in doc.rs
+    LpInterior(mc::interior_kind) // `LV.f` in doc.rs
 }
 
-/// a complete record of a loan that was granted
-pub struct Loan {
-    lp: @loan_path,
-    cmt: cmt,
-    kind: LoanKind
+pub impl LoanPath {
+    fn node_id(&self) -> ast::node_id {
+        match *self {
+            LpVar(local_id) => local_id,
+            LpExtend(base, _, _) => base.node_id()
+        }
+    }
 }
 
-/// maps computed by `gather_loans` that are then used by `check_loans`
-///
-/// - `req_loan_map`: map from each block/expr to the required loans needed
-///   for the duration of that block/expr
-/// - `pure_map`: map from block/expr that must be pure to the error message
-///   that should be reported if they are not pure
-pub struct ReqMaps {
-    req_loan_map: HashMap<ast::node_id, @mut ~[Loan]>,
-    pure_map: HashMap<ast::node_id, bckerr>
+pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> {
+    //! Computes the `LoanPath` (if any) for a `cmt`.
+    //! Note that this logic is somewhat duplicated in
+    //! the method `compute()` found in `gather_loans::restrictions`,
+    //! which allows it to share common loan path pieces as it
+    //! traverses the CMT.
+
+    match cmt.cat {
+        mc::cat_rvalue |
+        mc::cat_static_item |
+        mc::cat_copied_upvar(_) |
+        mc::cat_implicit_self |
+        mc::cat_arg(_, ast::by_ref) => {
+            None
+        }
+
+        mc::cat_local(id) |
+        mc::cat_arg(id, ast::by_copy) |
+        mc::cat_self(id) => {
+            Some(@LpVar(id))
+        }
+
+        mc::cat_deref(cmt_base, _, _) => {
+            opt_loan_path(cmt_base).map(
+                |&lp| @LpExtend(lp, cmt.mutbl, LpDeref))
+        }
+
+        mc::cat_interior(cmt_base, ik) => {
+            opt_loan_path(cmt_base).map(
+                |&lp| @LpExtend(lp, cmt.mutbl, LpInterior(ik)))
+        }
+
+        mc::cat_stack_upvar(cmt_base) |
+        mc::cat_discr(cmt_base, _) => {
+            opt_loan_path(cmt_base)
+        }
+    }
 }
 
-pub fn save_and_restore<T:Copy,U>(save_and_restore_t: &mut T,
-                                  f: &fn() -> U) -> U {
-    let old_save_and_restore_t = *save_and_restore_t;
-    let u = f();
-    *save_and_restore_t = old_save_and_restore_t;
-    u
+///////////////////////////////////////////////////////////////////////////
+// Restrictions
+//
+// Borrowing an lvalue often results in *restrictions* that limit what
+// can be done with this lvalue during the scope of the loan:
+//
+// - `RESTR_MUTATE`: The lvalue may not be modified and mutable pointers to
+//                   the value cannot be created.
+// - `RESTR_FREEZE`: Immutable pointers to the value cannot be created.
+// - `RESTR_ALIAS`: The lvalue may not be aliased in any way.
+//
+// In addition, no value which is restricted may be moved. Therefore,
+// restrictions are meaningful even if the RestrictionSet is empty,
+// because the restriction against moves is implied.
+
+pub struct Restriction {
+    loan_path: @LoanPath,
+    set: RestrictionSet
 }
 
-pub fn save_and_restore_managed<T:Copy,U>(save_and_restore_t: @mut T,
-                                          f: &fn() -> U) -> U {
-    let old_save_and_restore_t = *save_and_restore_t;
-    let u = f();
-    *save_and_restore_t = old_save_and_restore_t;
-    u
+pub struct RestrictionSet {
+    bits: u32
 }
 
-pub impl LoanKind {
-    fn is_freeze(&self) -> bool {
-        match *self {
-            TotalFreeze | PartialFreeze => true,
-            _ => false
-        }
+pub static RESTR_EMPTY: RestrictionSet  = RestrictionSet {bits: 0b000};
+pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b001};
+pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b010};
+pub static RESTR_ALIAS: RestrictionSet  = RestrictionSet {bits: 0b100};
+
+pub impl RestrictionSet {
+    fn intersects(&self, restr: RestrictionSet) -> bool {
+        (self.bits & restr.bits) != 0
     }
 
-    fn is_take(&self) -> bool {
-        match *self {
-            TotalTake | PartialTake => true,
-            _ => false
-        }
+    fn contains_all(&self, restr: RestrictionSet) -> bool {
+        (self.bits & restr.bits) == restr.bits
     }
 }
 
-/// Creates and returns a new root_map
+impl BitOr<RestrictionSet,RestrictionSet> for RestrictionSet {
+    fn bitor(&self, rhs: &RestrictionSet) -> RestrictionSet {
+        RestrictionSet {bits: self.bits | rhs.bits}
+    }
+}
 
-impl to_bytes::IterBytes for root_map_key {
-    fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) {
-        to_bytes::iter_bytes_2(&self.id, &self.derefs, lsb0, f);
+impl BitAnd<RestrictionSet,RestrictionSet> for RestrictionSet {
+    fn bitand(&self, rhs: &RestrictionSet) -> RestrictionSet {
+        RestrictionSet {bits: self.bits & rhs.bits}
     }
 }
 
+///////////////////////////////////////////////////////////////////////////
+// Rooting of managed boxes
+//
+// When we borrow the interior of a managed box, it is sometimes
+// necessary to *root* the box, meaning to stash a copy of the box
+// somewhere that the garbage collector will find it. This ensures
+// that the box is not collected for the lifetime of the borrow.
+//
+// As part of this rooting, we sometimes also freeze the box at
+// runtime, meaning that we dynamically detect when the box is
+// borrowed in incompatible ways.
+//
+// Both of these actions are driven through the `root_map`, which maps
+// from a node to the dynamic rooting action that should be taken when
+// that node executes. The node is identified through a
+// `root_map_key`, which pairs a node-id and a deref count---the
+// problem is that sometimes the box that needs to be rooted is only
+// uncovered after a certain number of auto-derefs.
+
+pub struct RootInfo {
+    scope: ast::node_id,
+    freeze: Option<DynaFreezeKind> // Some() if we should freeze box at runtime
+}
+
+pub type root_map = @mut HashMap<root_map_key, RootInfo>;
+
 pub fn root_map() -> root_map {
     return @mut HashMap::new();
 }
 
-// ___________________________________________________________________________
+pub enum DynaFreezeKind {
+    DynaImm,
+    DynaMut
+}
+
+impl ToStr for DynaFreezeKind {
+    fn to_str(&self) -> ~str {
+        match *self {
+            DynaMut => ~"mutable",
+            DynaImm => ~"immutable"
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Errors
+
+// Errors that can occur
+#[deriving(Eq)]
+pub enum bckerr_code {
+    err_mutbl(ast::mutability),
+    err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
+    err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
+    err_freeze_aliasable_const
+}
+
+// Combination of an error code and the categorization of the expression
+// that caused it
+#[deriving(Eq)]
+pub struct BckError {
+    span: span,
+    cmt: mc::cmt,
+    code: bckerr_code
+}
+
+pub enum AliasableViolationKind {
+    MutabilityViolation,
+    BorrowViolation
+}
+
+///////////////////////////////////////////////////////////////////////////
 // Misc
 
 pub impl BorrowckCtxt {
@@ -456,27 +399,31 @@ pub impl BorrowckCtxt {
         self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
     }
 
-    fn cat_expr(&self, expr: @ast::expr) -> cmt {
-        cat_expr(self.tcx, self.method_map, expr)
+    fn is_subscope_of(&self, r_sub: ast::node_id, r_sup: ast::node_id) -> bool {
+        self.tcx.region_maps.is_subscope_of(r_sub, r_sup)
+    }
+
+    fn cat_expr(&self, expr: @ast::expr) -> mc::cmt {
+        mc::cat_expr(self.tcx, self.method_map, expr)
     }
 
-    fn cat_expr_unadjusted(&self, expr: @ast::expr) -> cmt {
-        cat_expr_unadjusted(self.tcx, self.method_map, expr)
+    fn cat_expr_unadjusted(&self, expr: @ast::expr) -> mc::cmt {
+        mc::cat_expr_unadjusted(self.tcx, self.method_map, expr)
     }
 
     fn cat_expr_autoderefd(&self, expr: @ast::expr,
-                           adj: @ty::AutoAdjustment) -> cmt {
+                           adj: @ty::AutoAdjustment) -> mc::cmt {
         match *adj {
             ty::AutoAddEnv(*) => {
                 // no autoderefs
-                cat_expr_unadjusted(self.tcx, self.method_map, expr)
+                mc::cat_expr_unadjusted(self.tcx, self.method_map, expr)
             }
 
             ty::AutoDerefRef(
                 ty::AutoDerefRef {
                     autoderefs: autoderefs, _}) => {
-                cat_expr_autoderefd(self.tcx, self.method_map, expr,
-                                    autoderefs)
+                mc::cat_expr_autoderefd(self.tcx, self.method_map, expr,
+                                        autoderefs)
             }
         }
     }
@@ -485,43 +432,33 @@ pub impl BorrowckCtxt {
                id: ast::node_id,
                span: span,
                ty: ty::t,
-               def: ast::def) -> cmt {
-        cat_def(self.tcx, self.method_map, id, span, ty, def)
-    }
-
-    fn cat_variant<N:ast_node>(&self,
-                                arg: N,
-                                enum_did: ast::def_id,
-                                cmt: cmt) -> cmt {
-        cat_variant(self.tcx, self.method_map, arg, enum_did, cmt)
+               def: ast::def) -> mc::cmt {
+        mc::cat_def(self.tcx, self.method_map, id, span, ty, def)
     }
 
-    fn cat_discr(&self, cmt: cmt, match_id: ast::node_id) -> cmt {
-        return @cmt_ {cat:cat_discr(cmt, match_id),.. *cmt};
+    fn cat_discr(&self, cmt: mc::cmt, match_id: ast::node_id) -> mc::cmt {
+        @mc::cmt_ {cat:mc::cat_discr(cmt, match_id),
+                   mutbl:cmt.mutbl.inherit(),
+                   ..*cmt}
     }
 
-    fn mc_ctxt(&self) -> mem_categorization_ctxt {
-        mem_categorization_ctxt {tcx: self.tcx,
+    fn mc_ctxt(&self) -> mc::mem_categorization_ctxt {
+        mc::mem_categorization_ctxt {tcx: self.tcx,
                                  method_map: self.method_map}
     }
 
-    fn cat_pattern(&self, cmt: cmt, pat: @ast::pat, op: &fn(cmt, @ast::pat)) {
+    fn cat_pattern(&self,
+                   cmt: mc::cmt,
+                   pat: @ast::pat,
+                   op: &fn(mc::cmt, @ast::pat)) {
         let mc = self.mc_ctxt();
         mc.cat_pattern(cmt, pat, op);
     }
 
-    fn report_if_err(&self, bres: bckres<()>) {
-        match bres {
-          Ok(()) => (),
-          Err(ref e) => self.report((*e))
-        }
-    }
-
-    fn report(&self, err: bckerr) {
+    fn report(&self, err: BckError) {
         self.span_err(
-            err.cmt.span,
-            fmt!("illegal borrow: %s",
-                 self.bckerr_to_str(err)));
+            err.span,
+            self.bckerr_to_str(err));
         self.note_and_explain_bckerr(err);
     }
 
@@ -533,51 +470,75 @@ pub impl BorrowckCtxt {
         self.tcx.sess.span_note(s, m);
     }
 
-    fn add_to_mutbl_map(&self, cmt: cmt) {
-        match cmt.cat {
-          cat_local(id) | cat_arg(id) => {
-            self.mutbl_map.insert(id);
-          }
-          cat_stack_upvar(cmt) => {
-            self.add_to_mutbl_map(cmt);
-          }
-          _ => ()
-        }
-    }
-
-    fn bckerr_to_str(&self, err: bckerr) -> ~str {
+    fn bckerr_to_str(&self, err: BckError) -> ~str {
         match err.code {
             err_mutbl(lk) => {
-                fmt!("creating %s alias to %s",
-                     self.loan_kind_to_str(lk),
-                     self.cmt_to_str(err.cmt))
+                fmt!("cannot borrow %s %s as %s",
+                     err.cmt.mutbl.to_user_str(),
+                     self.cmt_to_str(err.cmt),
+                     self.mut_to_str(lk))
             }
-            err_mut_uniq => {
-                ~"unique value in aliasable, mutable location"
+            err_out_of_root_scope(*) => {
+                fmt!("cannot root managed value long enough")
             }
-            err_mut_variant => {
-                ~"enum variant in aliasable, mutable location"
+            err_out_of_scope(*) => {
+                fmt!("borrowed value does not live long enough")
             }
-            err_root_not_permitted => {
-                // note: I don't expect users to ever see this error
-                // message, reasons are discussed in attempt_root() in
-                // preserve.rs.
-                ~"rooting is not permitted"
+            err_freeze_aliasable_const => {
+                // Means that the user borrowed a ~T or enum value
+                // residing in &const or @const pointer.  Terrible
+                // error message, but then &const and @const are
+                // supposed to be going away.
+                fmt!("unsafe borrow of aliasable, const value")
             }
-            err_out_of_root_scope(*) => {
-                ~"cannot root managed value long enough"
+        }
+    }
+
+    fn report_aliasability_violation(&self,
+                                     span: span,
+                                     kind: AliasableViolationKind,
+                                     cause: mc::AliasableReason) {
+        let prefix = match kind {
+            MutabilityViolation => "cannot assign to an `&mut`",
+            BorrowViolation => "cannot borrow an `&mut`"
+        };
+
+        match cause {
+            mc::AliasableOther => {
+                self.tcx.sess.span_err(
+                    span,
+                    fmt!("%s in an aliasable location", prefix));
             }
-            err_out_of_scope(*) => {
-                ~"borrowed value does not live long enough"
+            mc::AliasableManaged(ast::m_mutbl) => {
+                // FIXME(#5074) we should prob do this borrow
+                self.tcx.sess.span_err(
+                    span,
+                    fmt!("%s in a `@mut` pointer; \
+                          try borrowing as `&mut` first", prefix));
+            }
+            mc::AliasableManaged(m) => {
+                self.tcx.sess.span_err(
+                    span,
+                    fmt!("%s in a `@%s` pointer; \
+                          try an `@mut` instead",
+                         prefix,
+                         self.mut_to_keyword(m)));
+            }
+            mc::AliasableBorrowed(m) => {
+                self.tcx.sess.span_err(
+                    span,
+                    fmt!("%s in a `&%s` pointer; \
+                          try an `&mut` instead",
+                         prefix,
+                         self.mut_to_keyword(m)));
             }
         }
     }
 
-    fn note_and_explain_bckerr(&self, err: bckerr) {
+    fn note_and_explain_bckerr(&self, err: BckError) {
         let code = err.code;
         match code {
-            err_mutbl(*) | err_mut_uniq | err_mut_variant |
-            err_root_not_permitted => {}
+            err_mutbl(*) | err_freeze_aliasable_const(*) => {}
 
             err_out_of_root_scope(super_scope, sub_scope) => {
                 note_and_explain_region(
@@ -607,46 +568,140 @@ pub impl BorrowckCtxt {
         }
     }
 
+    fn append_loan_path_to_str_from_interior(&self,
+                                             loan_path: &LoanPath,
+                                             out: &mut ~str) {
+        match *loan_path {
+            LpExtend(_, _, LpDeref) => {
+                str::push_char(out, '(');
+                self.append_loan_path_to_str(loan_path, out);
+                str::push_char(out, ')');
+            }
+            LpExtend(_, _, LpInterior(_)) |
+            LpVar(_) => {
+                self.append_loan_path_to_str(loan_path, out);
+            }
+        }
+    }
+
+    fn append_loan_path_to_str(&self, loan_path: &LoanPath, out: &mut ~str) {
+        match *loan_path {
+            LpVar(id) => {
+                match self.tcx.items.find(&id) {
+                    Some(&ast_map::node_local(ident)) => {
+                        str::push_str(out, *self.tcx.sess.intr().get(ident));
+                    }
+                    r => {
+                        self.tcx.sess.bug(
+                            fmt!("Loan path LpVar(%?) maps to %?, not local",
+                                 id, r));
+                    }
+                }
+            }
 
-    fn cmt_to_str(&self, cmt: cmt) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
-        mc.cmt_to_str(cmt)
+            LpExtend(lp_base, _, LpInterior(mc::interior_field(fld, _))) => {
+                self.append_loan_path_to_str_from_interior(lp_base, out);
+                str::push_char(out, '.');
+                str::push_str(out, *self.tcx.sess.intr().get(fld));
+            }
+
+            LpExtend(lp_base, _, LpInterior(mc::interior_index(*))) => {
+                self.append_loan_path_to_str_from_interior(lp_base, out);
+                str::push_str(out, "[]");
+            }
+
+            LpExtend(lp_base, _, LpInterior(mc::interior_tuple)) |
+            LpExtend(lp_base, _, LpInterior(mc::interior_anon_field)) |
+            LpExtend(lp_base, _, LpInterior(mc::interior_variant(_))) => {
+                self.append_loan_path_to_str_from_interior(lp_base, out);
+                str::push_str(out, ".(tuple)");
+            }
+
+            LpExtend(lp_base, _, LpDeref) => {
+                str::push_char(out, '*');
+                self.append_loan_path_to_str(lp_base, out);
+            }
+        }
+    }
+
+    fn loan_path_to_str(&self, loan_path: &LoanPath) -> ~str {
+        let mut result = ~"";
+        self.append_loan_path_to_str(loan_path, &mut result);
+        result
     }
 
-    fn cmt_to_repr(&self, cmt: cmt) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
-        mc.cmt_to_repr(cmt)
+    fn cmt_to_str(&self, cmt: mc::cmt) -> ~str {
+        let mc = &mc::mem_categorization_ctxt {tcx: self.tcx,
+                                               method_map: self.method_map};
+        mc.cmt_to_str(cmt)
     }
 
     fn mut_to_str(&self, mutbl: ast::mutability) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
+        let mc = &mc::mem_categorization_ctxt {tcx: self.tcx,
+                                               method_map: self.method_map};
         mc.mut_to_str(mutbl)
     }
 
-    fn loan_kind_to_str(&self, lk: LoanKind) -> ~str {
-        match lk {
-            TotalFreeze | PartialFreeze => ~"immutable",
-            TotalTake | PartialTake => ~"mutable",
-            Immobile => ~"read-only"
+    fn mut_to_keyword(&self, mutbl: ast::mutability) -> &'static str {
+        match mutbl {
+            ast::m_imm => "",
+            ast::m_const => "const",
+            ast::m_mutbl => "mut"
         }
     }
+}
+
+impl DataFlowOperator for LoanDataFlowOperator {
+    #[inline(always)]
+    fn initial_value(&self) -> bool {
+        false // no loans in scope by default
+    }
+
+    #[inline(always)]
+    fn join(&self, succ: uint, pred: uint) -> uint {
+        succ | pred // loans from both preds are in scope
+    }
+
+    #[inline(always)]
+    fn walk_closures(&self) -> bool {
+        true
+    }
+}
 
-    fn loan_to_repr(&self, loan: &Loan) -> ~str {
-        fmt!("Loan(lp=%?, cmt=%s, kind=%?)",
-             loan.lp, self.cmt_to_repr(loan.cmt), loan.kind)
+impl Repr for Loan {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        fmt!("Loan_%?(%s, %?, %?-%?, %s)",
+             self.index,
+             self.loan_path.repr(tcx),
+             self.mutbl,
+             self.gen_scope,
+             self.kill_scope,
+             self.restrictions.repr(tcx))
     }
 }
 
-// The inherent mutability of a component is its default mutability
-// assuming it is embedded in an immutable context.  In general, the
-// mutability can be "overridden" if the component is embedded in a
-// mutable structure.
-pub fn inherent_mutability(ck: comp_kind) -> mutability {
-    match ck {
-      comp_tuple | comp_anon_field | comp_variant(_) => m_imm,
-      comp_field(_, m) | comp_index(_, m)            => m
+impl Repr for Restriction {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        fmt!("Restriction(%s, %x)",
+             self.loan_path.repr(tcx),
+             self.set.bits as uint)
+    }
+}
+
+impl Repr for LoanPath {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        match self {
+            &LpVar(id) => {
+                fmt!("$(%?)", id)
+            }
+
+            &LpExtend(lp, _, LpDeref) => {
+                fmt!("%s.*", lp.repr(tcx))
+            }
+
+            &LpExtend(lp, _, LpInterior(ref interior)) => {
+                fmt!("%s.%s", lp.repr(tcx), interior.repr(tcx))
+            }
+        }
     }
 }
diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs
deleted file mode 100644
index c44920fffa5..00000000000
--- a/src/librustc/middle/borrowck/preserve.rs
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ----------------------------------------------------------------------
-// Preserve(Ex, S) holds if ToAddr(Ex) will remain valid for the entirety of
-// the scope S.
-//
-
-use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, BorrowckCtxt};
-use middle::borrowck::{err_mut_uniq, err_mut_variant};
-use middle::borrowck::{err_out_of_root_scope, err_out_of_scope};
-use middle::borrowck::{err_root_not_permitted, root_map_key};
-use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
-use middle::mem_categorization::{cat_discr, cat_local, cat_self, cat_special};
-use middle::mem_categorization::{cat_stack_upvar, cmt, comp_field};
-use middle::mem_categorization::{comp_index, comp_variant, gc_ptr};
-use middle::mem_categorization::{region_ptr};
-use middle::ty;
-use util::common::indenter;
-
-use syntax::ast;
-
-pub enum PreserveCondition {
-    PcOk,
-    PcIfPure(bckerr)
-}
-
-pub impl PreserveCondition {
-    // combines two preservation conditions such that if either of
-    // them requires purity, the result requires purity
-    fn combine(&self, pc: PreserveCondition) -> PreserveCondition {
-        match *self {
-            PcOk => {pc}
-            PcIfPure(_) => {*self}
-        }
-    }
-}
-
-pub impl BorrowckCtxt {
-    fn preserve(&self,
-                cmt: cmt,
-                scope_region: ty::Region,
-                item_ub: ast::node_id,
-                root_ub: ast::node_id) -> bckres<PreserveCondition>
-    {
-        let ctxt = PreserveCtxt {
-            bccx: self,
-            scope_region: scope_region,
-            item_ub: item_ub,
-            root_ub: root_ub,
-            root_managed_data: true
-        };
-        ctxt.preserve(cmt)
-    }
-}
-
-struct PreserveCtxt<'self> {
-    bccx: &'self BorrowckCtxt,
-
-    // the region scope for which we must preserve the memory
-    scope_region: ty::Region,
-
-    // the scope for the body of the enclosing fn/method item
-    item_ub: ast::node_id,
-
-    // the upper bound on how long we can root an @T pointer
-    root_ub: ast::node_id,
-
-    // if false, do not attempt to root managed data
-    root_managed_data: bool
-}
-
-pub impl<'self> PreserveCtxt<'self> {
-    fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
-
-    fn preserve(&self, cmt: cmt) -> bckres<PreserveCondition> {
-        debug!("preserve(cmt=%s, root_ub=%?, root_managed_data=%b)",
-               self.bccx.cmt_to_repr(cmt), self.root_ub,
-               self.root_managed_data);
-        let _i = indenter();
-
-        match cmt.cat {
-          cat_special(sk_implicit_self) |
-          cat_special(sk_heap_upvar) => {
-            self.compare_scope(cmt, ty::re_scope(self.item_ub))
-          }
-          cat_special(sk_static_item) | cat_special(sk_method) => {
-            Ok(PcOk)
-          }
-          cat_rvalue => {
-            // when we borrow an rvalue, we can keep it rooted but only
-            // up to the root_ub point
-
-            // When we're in a 'const &x = ...' context, self.root_ub is
-            // zero and the rvalue is static, not bound to a scope.
-            let scope_region = if self.root_ub == 0 {
-                ty::re_static
-            } else {
-                // Maybe if we pass in the parent instead here,
-                // we can prevent the "scope not found" error
-                debug!("scope_region thing: %? ", cmt.id);
-                self.tcx().region_maps.encl_region(cmt.id)
-            };
-
-            self.compare_scope(cmt, scope_region)
-          }
-          cat_stack_upvar(cmt) => {
-            self.preserve(cmt)
-          }
-          cat_local(local_id) => {
-            // Normally, local variables are lendable, and so this
-            // case should never trigger.  However, if we are
-            // preserving an expression like a.b where the field `b`
-            // has @ type, then it will recurse to ensure that the `a`
-            // is stable to try and avoid rooting the value `a.b`.  In
-            // this case, root_managed_data will be false.
-            if self.root_managed_data {
-                self.tcx().sess.span_bug(
-                    cmt.span,
-                    ~"preserve() called with local and !root_managed_data");
-            }
-            let local_region = self.tcx().region_maps.encl_region(local_id);
-            self.compare_scope(cmt, local_region)
-          }
-          cat_binding(local_id) => {
-            // Bindings are these kind of weird implicit pointers (cc
-            // #2329).  We require (in gather_loans) that they be
-            // rooted in an immutable location.
-            let local_region = self.tcx().region_maps.encl_region(local_id);
-            self.compare_scope(cmt, local_region)
-          }
-          cat_arg(local_id) => {
-            // This can happen as not all args are lendable (e.g., &&
-            // modes).  In that case, the caller guarantees stability
-            // for at least the scope of the fn.  This is basically a
-            // deref of a region ptr.
-            let local_region = self.tcx().region_maps.encl_region(local_id);
-            self.compare_scope(cmt, local_region)
-          }
-          cat_self(local_id) => {
-            let local_region = self.tcx().region_maps.encl_region(local_id);
-            self.compare_scope(cmt, local_region)
-          }
-          cat_comp(cmt_base, comp_field(*)) |
-          cat_comp(cmt_base, comp_index(*)) |
-          cat_comp(cmt_base, comp_tuple) |
-          cat_comp(cmt_base, comp_anon_field) => {
-            // Most embedded components: if the base is stable, the
-            // type never changes.
-            self.preserve(cmt_base)
-          }
-          cat_comp(cmt_base, comp_variant(enum_did)) => {
-            if ty::enum_is_univariant(self.tcx(), enum_did) {
-                self.preserve(cmt_base)
-            } else {
-                // If there are multiple variants: overwriting the
-                // base could cause the type of this memory to change,
-                // so require imm.
-                self.require_imm(cmt, cmt_base, err_mut_variant)
-            }
-          }
-          cat_deref(cmt_base, _, uniq_ptr) => {
-            // Overwriting the base could cause this memory to be
-            // freed, so require imm.
-            self.require_imm(cmt, cmt_base, err_mut_uniq)
-          }
-          cat_deref(_, _, region_ptr(_, region)) => {
-            // References are always "stable" for lifetime `region` by
-            // induction (when the reference of type &MT was created,
-            // the memory must have been stable).
-            self.compare_scope(cmt, region)
-          }
-          cat_deref(_, _, unsafe_ptr) => {
-            // Unsafe pointers are the user's problem
-            Ok(PcOk)
-          }
-          cat_deref(base, derefs, gc_ptr(*)) => {
-            // GC'd pointers of type @MT: if this pointer lives in
-            // immutable, stable memory, then everything is fine.  But
-            // otherwise we have no guarantee the pointer will stay
-            // live, so we must root the pointer (i.e., inc the ref
-            // count) for the duration of the loan.
-            debug!("base.mutbl = %?", base.mutbl);
-            if cmt.cat.derefs_through_mutable_box() {
-                self.attempt_root(cmt, base, derefs)
-            } else if base.mutbl.is_immutable() {
-                let non_rooting_ctxt = PreserveCtxt {
-                    root_managed_data: false,
-                    ..*self
-                };
-                match non_rooting_ctxt.preserve(base) {
-                  Ok(PcOk) => {
-                    Ok(PcOk)
-                  }
-                  Ok(PcIfPure(_)) => {
-                    debug!("must root @T, otherwise purity req'd");
-                    self.attempt_root(cmt, base, derefs)
-                  }
-                  Err(ref e) => {
-                    debug!("must root @T, err: %s",
-                           self.bccx.bckerr_to_str((*e)));
-                    self.attempt_root(cmt, base, derefs)
-                  }
-                }
-            } else {
-                self.attempt_root(cmt, base, derefs)
-            }
-          }
-          cat_discr(base, match_id) => {
-            // Subtle: in a match, we must ensure that each binding
-            // variable remains valid for the duration of the arm in
-            // which it appears, presuming that this arm is taken.
-            // But it is inconvenient in trans to root something just
-            // for one arm.  Therefore, we insert a cat_discr(),
-            // basically a special kind of category that says "if this
-            // value must be dynamically rooted, root it for the scope
-            // `match_id`.
-            //
-            // As an example, consider this scenario:
-            //
-            //    let mut x = @Some(3);
-            //    match *x { Some(y) {...} None {...} }
-            //
-            // Technically, the value `x` need only be rooted
-            // in the `some` arm.  However, we evaluate `x` in trans
-            // before we know what arm will be taken, so we just
-            // always root it for the duration of the match.
-            //
-            // As a second example, consider *this* scenario:
-            //
-            //    let x = @mut @Some(3);
-            //    match x { @@Some(y) {...} @@None {...} }
-            //
-            // Here again, `x` need only be rooted in the `some` arm.
-            // In this case, the value which needs to be rooted is
-            // found only when checking which pattern matches: but
-            // this check is done before entering the arm.  Therefore,
-            // even in this case we just choose to keep the value
-            // rooted for the entire match.  This means the value will be
-            // rooted even if the none arm is taken.  Oh well.
-            //
-            // At first, I tried to optimize the second case to only
-            // root in one arm, but the result was suboptimal: first,
-            // it interfered with the construction of phi nodes in the
-            // arm, as we were adding code to root values before the
-            // phi nodes were added.  This could have been addressed
-            // with a second basic block.  However, the naive approach
-            // also yielded suboptimal results for patterns like:
-            //
-            //    let x = @mut @...;
-            //    match x { @@some_variant(y) | @@some_other_variant(y) =>
-            //
-            // The reason is that we would root the value once for
-            // each pattern and not once per arm.  This is also easily
-            // fixed, but it's yet more code for what is really quite
-            // the corner case.
-            //
-            // Nonetheless, if you decide to optimize this case in the
-            // future, you need only adjust where the cat_discr()
-            // node appears to draw the line between what will be rooted
-            // in the *arm* vs the *match*.
-
-              let match_rooting_ctxt = PreserveCtxt {
-                  scope_region: ty::re_scope(match_id),
-                  ..*self
-              };
-              match_rooting_ctxt.preserve(base)
-          }
-        }
-    }
-
-    /// Reqiures that `cmt` (which is a deref or subcomponent of
-    /// `base`) be found in an immutable location (that is, `base`
-    /// must be immutable).  Also requires that `base` itself is
-    /// preserved.
-    fn require_imm(&self,
-                   cmt: cmt,
-                   cmt_base: cmt,
-                   code: bckerr_code) -> bckres<PreserveCondition> {
-        // Variant contents and unique pointers: must be immutably
-        // rooted to a preserved address.
-        match self.preserve(cmt_base) {
-          // the base is preserved, but if we are not mutable then
-          // purity is required
-          Ok(PcOk) => {
-              if !cmt_base.mutbl.is_immutable() {
-                  Ok(PcIfPure(bckerr {cmt:cmt, code:code}))
-              } else {
-                  Ok(PcOk)
-              }
-          }
-
-          // the base requires purity too, that's fine
-          Ok(PcIfPure(ref e)) => {
-            Ok(PcIfPure((*e)))
-          }
-
-          // base is not stable, doesn't matter
-          Err(ref e) => {
-            Err((*e))
-          }
-        }
-    }
-
-    /// Checks that the scope for which the value must be preserved
-    /// is a subscope of `scope_ub`; if so, success.
-    fn compare_scope(&self,
-                     cmt: cmt,
-                     scope_ub: ty::Region) -> bckres<PreserveCondition> {
-        if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
-            Ok(PcOk)
-        } else {
-            Err(bckerr {
-                cmt:cmt,
-                code:err_out_of_scope(scope_ub, self.scope_region)
-            })
-        }
-    }
-
-    /// Here, `cmt=*base` is always a deref of managed data (if
-    /// `derefs` != 0, then an auto-deref).  This routine determines
-    /// whether it is safe to MAKE cmt stable by rooting the pointer
-    /// `base`.  We can only do the dynamic root if the desired
-    /// lifetime `self.scope_region` is a subset of `self.root_ub`
-    /// scope; otherwise, it would either require that we hold the
-    /// value live for longer than the current fn or else potentially
-    /// require that an statically unbounded number of values be
-    /// rooted (if a loop exists).
-    fn attempt_root(&self, cmt: cmt, base: cmt,
-                    derefs: uint) -> bckres<PreserveCondition> {
-        if !self.root_managed_data {
-            // normally, there is a root_ub; the only time that this
-            // is none is when a boxed value is stored in an immutable
-            // location.  In that case, we will test to see if that
-            // immutable location itself can be preserved long enough
-            // in which case no rooting is necessary.  But there it
-            // would be sort of pointless to avoid rooting the inner
-            // box by rooting an outer box, as it would just keep more
-            // memory live than necessary, so we set root_ub to none.
-            return Err(bckerr { cmt: cmt, code: err_root_not_permitted });
-        }
-
-        let root_region = ty::re_scope(self.root_ub);
-        match self.scope_region {
-          // we can only root values if the desired region is some concrete
-          // scope within the fn body
-          ty::re_scope(scope_id) => {
-            debug!("Considering root map entry for %s: \
-                    node %d:%u -> scope_id %?, root_ub %?",
-                   self.bccx.cmt_to_repr(cmt), base.id,
-                   derefs, scope_id, self.root_ub);
-            if self.bccx.is_subregion_of(self.scope_region, root_region) {
-                debug!("Elected to root");
-                let rk = root_map_key { id: base.id, derefs: derefs };
-                // This code could potentially lead cause boxes to be frozen
-                // for longer than necessarily at runtime. It prevents an
-                // ICE in trans; the fundamental problem is that it's hard
-                // to make sure trans and borrowck have the same notion of
-                // scope. The real fix is to clean up how trans handles
-                // cleanups, but that's hard. If this becomes an issue, it's
-                // an option to just change this to `let scope_to_use =
-                // scope_id;`. Though that would potentially re-introduce
-                // the ICE. See #3511 for more details.
-                let scope_to_use = if
-                    self.bccx.stmt_map.contains(&scope_id) {
-                    // Root it in its parent scope, b/c
-                    // trans won't introduce a new scope for the
-                    // stmt
-                    self.root_ub
-                }
-                else {
-                    // Use the more precise scope
-                    scope_id
-                };
-                // We freeze if and only if this is a *mutable* @ box that
-                // we're borrowing into a pointer.
-                self.bccx.root_map.insert(rk, RootInfo {
-                    scope: scope_to_use,
-                    freezes: cmt.cat.derefs_through_mutable_box()
-                });
-                return Ok(PcOk);
-            } else {
-                debug!("Unable to root");
-                return Err(bckerr {
-                    cmt: cmt,
-                    code: err_out_of_root_scope(root_region,
-                                                self.scope_region)
-                });
-            }
-          }
-
-          // we won't be able to root long enough
-          _ => {
-              return Err(bckerr {
-                cmt:cmt,
-                code:err_out_of_root_scope(root_region, self.scope_region)
-              });
-          }
-
-        }
-    }
-}
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index bba4d35b560..dea08eedb61 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -185,9 +185,7 @@ pub fn lookup_const_by_id(tcx: ty::ctxt,
         }
     } else {
         let maps = astencode::Maps {
-            mutbl_map: @mut HashSet::new(),
             root_map: @mut HashMap::new(),
-            last_use_map: @mut HashMap::new(),
             method_map: @mut HashMap::new(),
             vtable_map: @mut HashMap::new(),
             write_guard_map: @mut HashSet::new(),
diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs
new file mode 100644
index 00000000000..cfdd7f95030
--- /dev/null
+++ b/src/librustc/middle/dataflow.rs
@@ -0,0 +1,1009 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+/*!
+ * A module for propagating forward dataflow information. The analysis
+ * assumes that the items to be propagated can be represented as bits
+ * and thus uses bitvectors. Your job is simply to specify the so-called
+ * GEN and KILL bits for each expression.
+ */
+
+use core::prelude::*;
+use core::cast;
+use core::uint;
+use syntax::ast;
+use syntax::ast_util;
+use syntax::ast_util::id_range;
+use syntax::print::{pp, pprust};
+use middle::ty;
+use middle::typeck;
+use util::ppaux::Repr;
+
+pub struct DataFlowContext<O> {
+    priv tcx: ty::ctxt,
+    priv method_map: typeck::method_map,
+
+    /// the data flow operator
+    priv oper: O,
+
+    /// range of ids that appear within the item in question
+    priv id_range: id_range,
+
+    /// number of bits to propagate per id
+    priv bits_per_id: uint,
+
+    /// number of words we will use to store bits_per_id.
+    /// equal to bits_per_id/uint::bits rounded up.
+    priv words_per_id: uint,
+
+    // Bit sets per id.  The following three fields (`gens`, `kills`,
+    // and `on_entry`) all have the same structure. For each id in
+    // `id_range`, there is a range of words equal to `words_per_id`.
+    // So, to access the bits for any given id, you take a slice of
+    // the full vector (see the method `compute_id_range()`).
+
+    /// bits generated as we exit the scope `id`. Updated by `add_gen()`.
+    priv gens: ~[uint],
+
+    /// bits killed as we exit the scope `id`. Updated by `add_kill()`.
+    priv kills: ~[uint],
+
+    /// bits that are valid on entry to the scope `id`. Updated by
+    /// `propagate()`.
+    priv on_entry: ~[uint]
+}
+
+/// Parameterization for the precise form of data flow that is used.
+pub trait DataFlowOperator {
+    /// Specifies the initial value for each bit in the `on_entry` set
+    fn initial_value(&self) -> bool;
+
+    /// Joins two predecessor bits together, typically either `|` or `&`
+    fn join(&self, succ: uint, pred: uint) -> uint;
+
+    /// True if we should propagate through closures
+    fn walk_closures(&self) -> bool;
+}
+
+struct PropagationContext<'self, O> {
+    dfcx: &'self mut DataFlowContext<O>,
+    changed: bool
+}
+
+#[deriving(Eq)]
+enum LoopKind {
+    /// A `while` or `loop` loop
+    TrueLoop,
+
+    /// A `for` "loop" (i.e., really a func call where `break`, `return`,
+    /// and `loop` all essentially perform an early return from the closure)
+    ForLoop
+}
+
+struct LoopScope<'self> {
+    loop_id: ast::node_id,
+    loop_kind: LoopKind,
+    break_bits: ~[uint]
+}
+
+impl<O:DataFlowOperator> DataFlowContext<O> {
+    pub fn new(tcx: ty::ctxt,
+               method_map: typeck::method_map,
+               oper: O,
+               id_range: id_range,
+               bits_per_id: uint) -> DataFlowContext<O> {
+        let words_per_id = (bits_per_id + uint::bits - 1) / uint::bits;
+
+        debug!("DataFlowContext::new(id_range=%?, bits_per_id=%?, words_per_id=%?)",
+               id_range, bits_per_id, words_per_id);
+
+        let len = (id_range.max - id_range.min) as uint * words_per_id;
+        let gens = vec::from_elem(len, 0);
+        let kills = vec::from_elem(len, 0);
+        let elem = if oper.initial_value() {uint::max_value} else {0};
+        let on_entry = vec::from_elem(len, elem);
+
+        DataFlowContext {
+            tcx: tcx,
+            method_map: method_map,
+            words_per_id: words_per_id,
+            bits_per_id: bits_per_id,
+            oper: oper,
+            id_range: id_range,
+            gens: gens,
+            kills: kills,
+            on_entry: on_entry
+        }
+    }
+
+    pub fn add_gen(&mut self, id: ast::node_id, bit: uint) {
+        //! Indicates that `id` generates `bit`
+
+        debug!("add_gen(id=%?, bit=%?)", id, bit);
+        let (start, end) = self.compute_id_range(id);
+        {
+            let gens = vec::mut_slice(self.gens, start, end);
+            set_bit(gens, bit);
+        }
+    }
+
+    pub fn add_kill(&mut self, id: ast::node_id, bit: uint) {
+        //! Indicates that `id` kills `bit`
+
+        debug!("add_kill(id=%?, bit=%?)", id, bit);
+        let (start, end) = self.compute_id_range(id);
+        {
+            let kills = vec::mut_slice(self.kills, start, end);
+            set_bit(kills, bit);
+        }
+    }
+
+    fn apply_gen_kill(&self, id: ast::node_id, bits: &mut [uint]) {
+        //! Applies the gen and kill sets for `id` to `bits`
+
+        debug!("apply_gen_kill(id=%?, bits=%s) [before]",
+               id, mut_bits_to_str(bits));
+        let (start, end) = self.compute_id_range(id);
+        let gens = self.gens.slice(start, end);
+        bitwise(bits, gens, |a, b| a | b);
+        let kills = self.kills.slice(start, end);
+        bitwise(bits, kills, |a, b| a & !b);
+
+        debug!("apply_gen_kill(id=%?, bits=%s) [after]",
+               id, mut_bits_to_str(bits));
+    }
+
+    fn apply_kill(&self, id: ast::node_id, bits: &mut [uint]) {
+        debug!("apply_kill(id=%?, bits=%s) [before]",
+               id, mut_bits_to_str(bits));
+        let (start, end) = self.compute_id_range(id);
+        let kills = self.kills.slice(start, end);
+        bitwise(bits, kills, |a, b| a & !b);
+        debug!("apply_kill(id=%?, bits=%s) [after]",
+               id, mut_bits_to_str(bits));
+    }
+
+    fn compute_id_range(&self, absolute_id: ast::node_id) -> (uint, uint) {
+        assert!(absolute_id >= self.id_range.min);
+        assert!(absolute_id < self.id_range.max);
+
+        let relative_id = absolute_id - self.id_range.min;
+        let start = (relative_id as uint) * self.words_per_id;
+        let end = start + self.words_per_id;
+        (start, end)
+    }
+
+
+    pub fn each_bit_on_entry(&self,
+                             id: ast::node_id,
+                             f: &fn(uint) -> bool) {
+        //! Iterates through each bit that is set on entry to `id`.
+        //! Only useful after `propagate()` has been called.
+
+        let (start, end) = self.compute_id_range(id);
+        let on_entry = vec::slice(self.on_entry, start, end);
+        debug!("each_bit_on_entry(id=%?, on_entry=%s)",
+               id, bits_to_str(on_entry));
+        self.each_bit(on_entry, f);
+    }
+
+    pub fn each_gen_bit(&self,
+                        id: ast::node_id,
+                        f: &fn(uint) -> bool) {
+        //! Iterates through each bit in the gen set for `id`.
+
+        let (start, end) = self.compute_id_range(id);
+        let gens = vec::slice(self.gens, start, end);
+        debug!("each_gen_bit(id=%?, gens=%s)",
+               id, bits_to_str(gens));
+        self.each_bit(gens, f)
+    }
+
+    fn each_bit(&self,
+                words: &[uint],
+                f: &fn(uint) -> bool) {
+        //! Helper for iterating over the bits in a bit set.
+
+        for words.eachi |word_index, &word| {
+            if word != 0 {
+                let base_index = word_index * uint::bits;
+                for uint::range(0, uint::bits) |offset| {
+                    let bit = 1 << offset;
+                    if (word & bit) != 0 {
+                        // NB: we round up the total number of bits
+                        // that we store in any given bit set so that
+                        // it is an even multiple of uint::bits.  This
+                        // means that there may be some stray bits at
+                        // the end that do not correspond to any
+                        // actual value.  So before we callback, check
+                        // whether the bit_index is greater than the
+                        // actual value the user specified and stop
+                        // iterating if so.
+                        let bit_index = base_index + offset;
+                        if bit_index >= self.bits_per_id || !f(bit_index) {
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl<O:DataFlowOperator+Copy+'static> DataFlowContext<O> {
+//                      ^^^^^^^^^^^^ only needed for pretty printing
+    pub fn propagate(&mut self, blk: &ast::blk) {
+        //! Performs the data flow analysis.
+
+        if self.bits_per_id == 0 {
+            // Optimize the surprisingly common degenerate case.
+            return;
+        }
+
+        let mut propcx = PropagationContext {
+            dfcx: self,
+            changed: true
+        };
+
+        let mut temp = vec::from_elem(self.words_per_id, 0);
+        let mut loop_scopes = ~[];
+
+        while propcx.changed {
+            propcx.changed = false;
+            propcx.reset(temp);
+            propcx.walk_block(blk, temp, &mut loop_scopes);
+        }
+
+        debug!("Dataflow result:");
+        debug!("%s", {
+            let this = @copy *self;
+            this.pretty_print_to(io::stderr(), blk);
+            ""
+        });
+    }
+
+    fn pretty_print_to(@self, wr: @io::Writer, blk: &ast::blk) {
+        let pre: @fn(pprust::ann_node) = |node| {
+            let (ps, id) = match node {
+                pprust::node_expr(ps, expr) => (ps, expr.id),
+                pprust::node_block(ps, blk) => (ps, blk.node.id),
+                pprust::node_item(ps, _) => (ps, 0),
+                pprust::node_pat(ps, pat) => (ps, pat.id)
+            };
+
+            if id >= self.id_range.min || id < self.id_range.max {
+                let (start, end) = self.compute_id_range(id);
+                let on_entry = vec::slice(self.on_entry, start, end);
+                let entry_str = bits_to_str(on_entry);
+
+                let gens = vec::slice(self.gens, start, end);
+                let gens_str = if gens.any(|&u| u != 0) {
+                    fmt!(" gen: %s", bits_to_str(gens))
+                } else {
+                    ~""
+                };
+
+                let kills = vec::slice(self.kills, start, end);
+                let kills_str = if kills.any(|&u| u != 0) {
+                    fmt!(" kill: %s", bits_to_str(kills))
+                } else {
+                    ~""
+                };
+
+                let comment_str = fmt!("id %d: %s%s%s",
+                                       id, entry_str, gens_str, kills_str);
+                pprust::synth_comment(ps, comment_str);
+                pp::space(ps.s);
+            }
+        };
+
+        let post: @fn(pprust::ann_node) = |_| {
+        };
+
+        let ps = pprust::rust_printer_annotated(
+            wr, self.tcx.sess.intr(),
+            pprust::pp_ann {pre:pre, post:post});
+        pprust::cbox(ps, pprust::indent_unit);
+        pprust::ibox(ps, 0u);
+        pprust::print_block(ps, blk);
+        pp::eof(ps.s);
+    }
+}
+
+impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
+    fn tcx(&self) -> ty::ctxt {
+        self.dfcx.tcx
+    }
+
+    fn walk_block(&mut self,
+                  blk: &ast::blk,
+                  in_out: &mut [uint],
+                  loop_scopes: &mut ~[LoopScope]) {
+        debug!("DataFlowContext::walk_block(blk.node.id=%?, in_out=%s)",
+               blk.node.id, bits_to_str(reslice(in_out)));
+
+        self.merge_with_entry_set(blk.node.id, in_out);
+
+        for blk.node.stmts.each |&stmt| {
+            self.walk_stmt(stmt, in_out, loop_scopes);
+        }
+
+        self.walk_opt_expr(blk.node.expr, in_out, loop_scopes);
+
+        self.dfcx.apply_gen_kill(blk.node.id, in_out);
+    }
+
+    fn walk_stmt(&mut self,
+                 stmt: @ast::stmt,
+                 in_out: &mut [uint],
+                 loop_scopes: &mut ~[LoopScope]) {
+        match stmt.node {
+            ast::stmt_decl(decl, _) => {
+                self.walk_decl(decl, in_out, loop_scopes);
+            }
+
+            ast::stmt_expr(expr, _) | ast::stmt_semi(expr, _) => {
+                self.walk_expr(expr, in_out, loop_scopes);
+            }
+
+            ast::stmt_mac(*) => {
+                self.tcx().sess.span_bug(stmt.span, ~"unexpanded macro");
+            }
+        }
+    }
+
+    fn walk_decl(&mut self,
+                 decl: @ast::decl,
+                 in_out: &mut [uint],
+                 loop_scopes: &mut ~[LoopScope]) {
+        match decl.node {
+            ast::decl_local(ref locals) => {
+                for locals.each |local| {
+                    self.walk_pat(local.node.pat, in_out, loop_scopes);
+                    self.walk_opt_expr(local.node.init, in_out, loop_scopes);
+                }
+            }
+
+            ast::decl_item(_) => {}
+        }
+    }
+
+    fn walk_expr(&mut self,
+                 expr: @ast::expr,
+                 in_out: &mut [uint],
+                 loop_scopes: &mut ~[LoopScope]) {
+        debug!("DataFlowContext::walk_expr(expr=%s, in_out=%s)",
+               expr.repr(self.dfcx.tcx), bits_to_str(reslice(in_out)));
+
+        self.merge_with_entry_set(expr.id, in_out);
+
+        match expr.node {
+            ast::expr_fn_block(ref decl, ref body) => {
+                if self.dfcx.oper.walk_closures() {
+                    // In the absence of once fns, we must assume that
+                    // every function body will execute more than
+                    // once. Thus we treat every function body like a
+                    // loop.
+                    //
+                    // What is subtle and a bit tricky, also, is how
+                    // to deal with the "output" bits---that is, what
+                    // do we consider to be the successor of a
+                    // function body, given that it could be called
+                    // from any point within its lifetime? What we do
+                    // is to add their effects immediately as of the
+                    // point of creation. Of course we have to ensure
+                    // that this is sound for the analyses which make
+                    // use of dataflow.
+                    //
+                    // In the case of the initedness checker (which
+                    // does not currently use dataflow, but I hope to
+                    // convert at some point), we will simply not walk
+                    // closures at all, so it's a moot point.
+                    //
+                    // In the case of the borrow checker, this means
+                    // the loans which would be created by calling a
+                    // function come into effect immediately when the
+                    // function is created. This is guaranteed to be
+                    // earlier than the point at which the loan
+                    // actually comes into scope (which is the point
+                    // at which the closure is *called*). Because
+                    // loans persist until the scope of the loans is
+                    // exited, it is always a safe approximation to
+                    // have a loan begin earlier than it actually will
+                    // at runtime, so this should be sound.
+                    //
+                    // We stil have to be careful in the region
+                    // checker and borrow checker to treat function
+                    // bodies like loops, which implies some
+                    // limitations. For example, a closure cannot root
+                    // a managed box for longer than its body.
+                    //
+                    // General control flow looks like this:
+                    //
+                    //  +- (expr) <----------+
+                    //  |    |               |
+                    //  |    v               |
+                    //  |  (body) -----------+--> (exit)
+                    //  |    |               |
+                    //  |    + (break/loop) -+
+                    //  |                    |
+                    //  +--------------------+
+                    //
+                    // This is a bit more conservative than a loop.
+                    // Note that we must assume that even after a
+                    // `break` occurs (e.g., in a `for` loop) that the
+                    // closure may be reinvoked.
+                    //
+                    // One difference from other loops is that `loop`
+                    // and `break` statements which target a closure
+                    // both simply add to the `break_bits`.
+
+                    // func_bits represents the state when the function
+                    // returns
+                    let mut func_bits = reslice(in_out).to_vec();
+
+                    loop_scopes.push(LoopScope {
+                        loop_id: expr.id,
+                        loop_kind: ForLoop,
+                        break_bits: reslice(in_out).to_vec()
+                    });
+                    for decl.inputs.each |input| {
+                        self.walk_pat(input.pat, func_bits, loop_scopes);
+                    }
+                    self.walk_block(body, func_bits, loop_scopes);
+
+                    // add the bits from any early return via `break`,
+                    // `continue`, or `return` into `func_bits`
+                    let loop_scope = loop_scopes.pop();
+                    join_bits(&self.dfcx.oper, loop_scope.break_bits, func_bits);
+
+                    // add `func_bits` to the entry bits for `expr`,
+                    // since we must assume the function may be called
+                    // more than once
+                    self.add_to_entry_set(expr.id, reslice(func_bits));
+
+                    // the final exit bits include whatever was present
+                    // in the original, joined with the bits from the function
+                    join_bits(&self.dfcx.oper, func_bits, in_out);
+                }
+            }
+
+            ast::expr_if(cond, ref then, els) => {
+                //
+                //     (cond)
+                //       |
+                //       v
+                //      ( )
+                //     /   \
+                //    |     |
+                //    v     v
+                //  (then)(els)
+                //    |     |
+                //    v     v
+                //   (  succ  )
+                //
+                self.walk_expr(cond, in_out, loop_scopes);
+
+                let mut then_bits = reslice(in_out).to_vec();
+                self.walk_block(then, then_bits, loop_scopes);
+
+                self.walk_opt_expr(els, in_out, loop_scopes);
+                join_bits(&self.dfcx.oper, then_bits, in_out);
+            }
+
+            ast::expr_while(cond, ref blk) => {
+                //
+                //     (expr) <--+
+                //       |       |
+                //       v       |
+                //  +--(cond)    |
+                //  |    |       |
+                //  |    v       |
+                //  v  (blk) ----+
+                //       |
+                //    <--+ (break)
+                //
+
+                self.walk_expr(cond, in_out, loop_scopes);
+
+                let mut body_bits = reslice(in_out).to_vec();
+                loop_scopes.push(LoopScope {
+                    loop_id: expr.id,
+                    loop_kind: TrueLoop,
+                    break_bits: reslice(in_out).to_vec()
+                });
+                self.walk_block(blk, body_bits, loop_scopes);
+                self.add_to_entry_set(expr.id, body_bits);
+                let new_loop_scope = loop_scopes.pop();
+                copy_bits(new_loop_scope.break_bits, in_out);
+            }
+
+            ast::expr_loop(ref blk, _) => {
+                //
+                //     (expr) <--+
+                //       |       |
+                //       v       |
+                //     (blk) ----+
+                //       |
+                //    <--+ (break)
+                //
+
+                let mut body_bits = reslice(in_out).to_vec();
+                self.reset(in_out);
+                loop_scopes.push(LoopScope {
+                    loop_id: expr.id,
+                    loop_kind: TrueLoop,
+                    break_bits: reslice(in_out).to_vec()
+                });
+                self.walk_block(blk, body_bits, loop_scopes);
+                self.add_to_entry_set(expr.id, body_bits);
+
+                let new_loop_scope = loop_scopes.pop();
+                assert_eq!(new_loop_scope.loop_id, expr.id);
+                copy_bits(new_loop_scope.break_bits, in_out);
+            }
+
+            ast::expr_match(discr, ref arms) => {
+                //
+                //    (discr)
+                //     / | \
+                //    |  |  |
+                //    v  v  v
+                //   (..arms..)
+                //    |  |  |
+                //    v  v  v
+                //   (  succ  )
+                //
+                //
+                self.walk_expr(discr, in_out, loop_scopes);
+
+                let mut guards = reslice(in_out).to_vec();
+
+                // We know that exactly one arm will be taken, so we
+                // can start out with a blank slate and just union
+                // together the bits from each arm:
+                self.reset(in_out);
+
+                for arms.each |arm| {
+                    // in_out reflects the discr and all guards to date
+                    self.walk_opt_expr(arm.guard, guards, loop_scopes);
+
+                    // determine the bits for the body and then union
+                    // them into `in_out`, which reflects all bodies to date
+                    let mut body = reslice(guards).to_vec();
+                    self.walk_pat_alternatives(arm.pats, body, loop_scopes);
+                    self.walk_block(&arm.body, body, loop_scopes);
+                    join_bits(&self.dfcx.oper, body, in_out);
+                }
+            }
+
+            ast::expr_ret(o_e) => {
+                self.walk_opt_expr(o_e, in_out, loop_scopes);
+
+                // is this a return from a `for`-loop closure?
+                match loop_scopes.position(|s| s.loop_kind == ForLoop) {
+                    Some(i) => {
+                        // if so, add the in_out bits to the state
+                        // upon exit. Remember that we cannot count
+                        // upon the `for` loop function not to invoke
+                        // the closure again etc.
+                        self.break_from_to(expr, &mut loop_scopes[i], in_out);
+                    }
+
+                    None => {}
+                }
+
+                self.reset(in_out);
+            }
+
+            ast::expr_break(label) => {
+                let scope = self.find_scope(expr, label, loop_scopes);
+                self.break_from_to(expr, scope, in_out);
+                self.reset(in_out);
+            }
+
+            ast::expr_again(label) => {
+                let scope = self.find_scope(expr, label, loop_scopes);
+
+                match scope.loop_kind {
+                    TrueLoop => {
+                        self.pop_scopes(expr, scope, in_out);
+                        self.add_to_entry_set(scope.loop_id, reslice(in_out));
+                    }
+
+                    ForLoop => {
+                        // If this `loop` construct is looping back to a `for`
+                        // loop, then `loop` is really just a return from the
+                        // closure. Therefore, we treat it the same as `break`.
+                        // See case for `expr_fn_block` for more details.
+                        self.break_from_to(expr, scope, in_out);
+                    }
+                }
+
+                self.reset(in_out);
+            }
+
+            ast::expr_assign(l, r) |
+            ast::expr_assign_op(_, l, r) => {
+                self.walk_expr(r, in_out, loop_scopes);
+                self.walk_expr(l, in_out, loop_scopes);
+            }
+
+            ast::expr_swap(l, r) => {
+                self.walk_expr(l, in_out, loop_scopes);
+                self.walk_expr(r, in_out, loop_scopes);
+            }
+
+            ast::expr_vec(ref exprs, _) => {
+                self.walk_exprs(*exprs, in_out, loop_scopes)
+            }
+
+            ast::expr_repeat(l, r, _) => {
+                self.walk_expr(l, in_out, loop_scopes);
+                self.walk_expr(r, in_out, loop_scopes);
+            }
+
+            ast::expr_struct(_, ref fields, with_expr) => {
+                self.walk_opt_expr(with_expr, in_out, loop_scopes);
+                for fields.each |field| {
+                    self.walk_expr(field.node.expr, in_out, loop_scopes);
+                }
+            }
+
+            ast::expr_call(f, ref args, _) => {
+                self.walk_call(expr.callee_id, expr.id,
+                               f, *args, in_out, loop_scopes);
+            }
+
+            ast::expr_method_call(rcvr, _, _, ref args, _) => {
+                self.walk_call(expr.callee_id, expr.id,
+                               rcvr, *args, in_out, loop_scopes);
+            }
+
+            ast::expr_index(l, r) |
+            ast::expr_binary(_, l, r) if self.is_method_call(expr) => {
+                self.walk_call(expr.callee_id, expr.id,
+                               l, [r], in_out, loop_scopes);
+            }
+
+            ast::expr_unary(_, e) if self.is_method_call(expr) => {
+                self.walk_call(expr.callee_id, expr.id,
+                               e, [], in_out, loop_scopes);
+            }
+
+            ast::expr_tup(ref exprs) => {
+                self.walk_exprs(*exprs, in_out, loop_scopes);
+            }
+
+            ast::expr_binary(op, l, r) if ast_util::lazy_binop(op) => {
+                self.walk_expr(l, in_out, loop_scopes);
+                let temp = reslice(in_out).to_vec();
+                self.walk_expr(r, in_out, loop_scopes);
+                join_bits(&self.dfcx.oper, temp, in_out);
+            }
+
+            ast::expr_log(l, r) |
+            ast::expr_index(l, r) |
+            ast::expr_binary(_, l, r) => {
+                self.walk_exprs([l, r], in_out, loop_scopes);
+            }
+
+            ast::expr_lit(*) |
+            ast::expr_path(*) => {
+            }
+
+            ast::expr_addr_of(_, e) |
+            ast::expr_copy(e) |
+            ast::expr_loop_body(e) |
+            ast::expr_do_body(e) |
+            ast::expr_cast(e, _) |
+            ast::expr_unary(_, e) |
+            ast::expr_paren(e) |
+            ast::expr_vstore(e, _) |
+            ast::expr_field(e, _, _) => {
+                self.walk_expr(e, in_out, loop_scopes);
+            }
+
+            ast::expr_inline_asm(ref inline_asm) => {
+                for inline_asm.inputs.each |&(_, expr)| {
+                    self.walk_expr(expr, in_out, loop_scopes);
+                }
+                for inline_asm.outputs.each |&(_, expr)| {
+                    self.walk_expr(expr, in_out, loop_scopes);
+                }
+            }
+
+            ast::expr_block(ref blk) => {
+                self.walk_block(blk, in_out, loop_scopes);
+            }
+
+            ast::expr_mac(*) => {
+                self.tcx().sess.span_bug(expr.span, ~"unexpanded macro");
+            }
+        }
+
+        self.dfcx.apply_gen_kill(expr.id, in_out);
+    }
+
+    fn pop_scopes(&mut self,
+                  from_expr: @ast::expr,
+                  to_scope: &mut LoopScope,
+                  in_out: &mut [uint]) {
+        //! Whenever you have a `break` or a `loop` statement, flow
+        //! exits through any number of enclosing scopes on its
+        //! way to the new destination. This function applies the kill
+        //! sets of those enclosing scopes to `in_out` (those kill sets
+        //! concern items that are going out of scope).
+
+        let tcx = self.tcx();
+        let region_maps = tcx.region_maps;
+
+        debug!("pop_scopes(from_expr=%s, to_scope=%?, in_out=%s)",
+               from_expr.repr(tcx), to_scope.loop_id,
+               bits_to_str(reslice(in_out)));
+
+        let mut id = from_expr.id;
+        while id != to_scope.loop_id {
+            self.dfcx.apply_kill(id, in_out);
+
+            match region_maps.opt_encl_scope(id) {
+                Some(i) => { id = i; }
+                None => {
+                    tcx.sess.span_bug(
+                        from_expr.span,
+                        fmt!("pop_scopes(from_expr=%s, to_scope=%?) \
+                              to_scope does not enclose from_expr",
+                             from_expr.repr(tcx), to_scope.loop_id));
+                }
+            }
+        }
+    }
+
+    fn break_from_to(&mut self,
+                     from_expr: @ast::expr,
+                     to_scope: &mut LoopScope,
+                     in_out: &mut [uint]) {
+        self.pop_scopes(from_expr, to_scope, in_out);
+        self.dfcx.apply_kill(from_expr.id, in_out);
+        join_bits(&self.dfcx.oper, reslice(in_out), to_scope.break_bits);
+        debug!("break_from_to(from_expr=%s, to_scope=%?) final break_bits=%s",
+               from_expr.repr(self.tcx()),
+               to_scope.loop_id,
+               bits_to_str(reslice(in_out)));
+    }
+
+    fn walk_exprs(&mut self,
+                  exprs: &[@ast::expr],
+                  in_out: &mut [uint],
+                  loop_scopes: &mut ~[LoopScope]) {
+        for exprs.each |&expr| {
+            self.walk_expr(expr, in_out, loop_scopes);
+        }
+    }
+
+    fn walk_opt_expr(&mut self,
+                     opt_expr: Option<@ast::expr>,
+                     in_out: &mut [uint],
+                     loop_scopes: &mut ~[LoopScope]) {
+        for opt_expr.each |&expr| {
+            self.walk_expr(expr, in_out, loop_scopes);
+        }
+    }
+
+    fn walk_call(&mut self,
+                 _callee_id: ast::node_id,
+                 call_id: ast::node_id,
+                 arg0: @ast::expr,
+                 args: &[@ast::expr],
+                 in_out: &mut [uint],
+                 loop_scopes: &mut ~[LoopScope]) {
+        self.walk_expr(arg0, in_out, loop_scopes);
+        self.walk_exprs(args, in_out, loop_scopes);
+
+        // FIXME(#5074) nested method calls
+        // self.merge_with_entry_set(callee_id, in_out);
+        // self.dfcx.apply_gen_kill(callee_id, in_out);
+
+        let return_ty = ty::node_id_to_type(self.tcx(), call_id);
+        let fails = ty::type_is_bot(return_ty);
+        if fails {
+            self.reset(in_out);
+        }
+    }
+
+    fn walk_pat(&mut self,
+                pat: @ast::pat,
+                in_out: &mut [uint],
+                _loop_scopes: &mut ~[LoopScope]) {
+        debug!("DataFlowContext::walk_pat(pat=%s, in_out=%s)",
+               pat.repr(self.dfcx.tcx), bits_to_str(reslice(in_out)));
+
+        do ast_util::walk_pat(pat) |p| {
+            debug!("  p.id=%? in_out=%s", p.id, bits_to_str(reslice(in_out)));
+            self.merge_with_entry_set(p.id, in_out);
+            self.dfcx.apply_gen_kill(p.id, in_out);
+        }
+    }
+
+    fn walk_pat_alternatives(&mut self,
+                             pats: &[@ast::pat],
+                             in_out: &mut [uint],
+                             loop_scopes: &mut ~[LoopScope]) {
+        if pats.len() == 1 {
+            // Common special case:
+            return self.walk_pat(pats[0], in_out, loop_scopes);
+        }
+
+        // In the general case, the patterns in `pats` are
+        // alternatives, so we must treat this like an N-way select
+        // statement.
+        let initial_state = reslice(in_out).to_vec();
+        self.reset(in_out);
+        for pats.each |&pat| {
+            let mut temp = copy initial_state;
+            self.walk_pat(pat, in_out, loop_scopes);
+            join_bits(&self.dfcx.oper, temp, in_out);
+        }
+    }
+
+    fn find_scope<'a>(&self,
+                      expr: @ast::expr,
+                      label: Option<ast::ident>,
+                      loop_scopes: &'a mut ~[LoopScope]) -> &'a mut LoopScope {
+        let index = match label {
+            None => {
+                let len = loop_scopes.len();
+                len - 1
+            }
+
+            Some(_) => {
+                match self.tcx().def_map.find(&expr.id) {
+                    Some(&ast::def_label(loop_id)) => {
+                        match loop_scopes.position(|l| l.loop_id == loop_id) {
+                            Some(i) => i,
+                            None => {
+                                self.tcx().sess.span_bug(
+                                    expr.span,
+                                    fmt!("No loop scope for id %?", loop_id));
+                            }
+                        }
+                    }
+
+                    r => {
+                        self.tcx().sess.span_bug(
+                            expr.span,
+                            fmt!("Bad entry `%?` in def_map for label", r));
+                    }
+                }
+            }
+        };
+
+        &mut loop_scopes[index]
+    }
+
+    fn is_method_call(&self, expr: @ast::expr) -> bool {
+        self.dfcx.method_map.contains_key(&expr.id)
+    }
+
+    fn reset(&mut self, bits: &mut [uint]) {
+        let e = if self.dfcx.oper.initial_value() {uint::max_value} else {0};
+        for vec::each_mut(bits) |b| { *b = e; }
+    }
+
+    fn add_to_entry_set(&mut self, id: ast::node_id, pred_bits: &[uint]) {
+        debug!("add_to_entry_set(id=%?, pred_bits=%s)",
+               id, bits_to_str(pred_bits));
+        let (start, end) = self.dfcx.compute_id_range(id);
+        let changed = { // FIXME(#5074) awkward construction
+            let on_entry = vec::mut_slice(self.dfcx.on_entry, start, end);
+            join_bits(&self.dfcx.oper, pred_bits, on_entry)
+        };
+        if changed {
+            debug!("changed entry set for %? to %s",
+                   id, bits_to_str(self.dfcx.on_entry.slice(start, end)));
+            self.changed = true;
+        }
+    }
+
+    fn merge_with_entry_set(&mut self,
+                            id: ast::node_id,
+                            pred_bits: &mut [uint]) {
+        debug!("merge_with_entry_set(id=%?, pred_bits=%s)",
+               id, mut_bits_to_str(pred_bits));
+        let (start, end) = self.dfcx.compute_id_range(id);
+        let changed = { // FIXME(#5074) awkward construction
+            let on_entry = vec::mut_slice(self.dfcx.on_entry, start, end);
+            let changed = join_bits(&self.dfcx.oper, reslice(pred_bits), on_entry);
+            copy_bits(reslice(on_entry), pred_bits);
+            changed
+        };
+        if changed {
+            debug!("changed entry set for %? to %s",
+                   id, bits_to_str(self.dfcx.on_entry.slice(start, end)));
+            self.changed = true;
+        }
+    }
+}
+
+fn mut_bits_to_str(words: &mut [uint]) -> ~str {
+    bits_to_str(reslice(words))
+}
+
+fn bits_to_str(words: &[uint]) -> ~str {
+    let mut result = ~"";
+    let mut sep = '[';
+
+    // Note: this is a little endian printout of bytes.
+
+    for words.each |&word| {
+        let mut v = word;
+        for uint::range(0, uint::bytes) |_| {
+            str::push_char(&mut result, sep);
+            str::push_str(&mut result, fmt!("%02x", v & 0xFF));
+            v >>= 8;
+            sep = '-';
+        }
+    }
+    str::push_char(&mut result, ']');
+    return result;
+}
+
+fn copy_bits(in_vec: &[uint], out_vec: &mut [uint]) -> bool {
+    bitwise(out_vec, in_vec, |_, b| b)
+}
+
+fn join_bits<O:DataFlowOperator>(oper: &O,
+                                 in_vec: &[uint],
+                                 out_vec: &mut [uint]) -> bool {
+    bitwise(out_vec, in_vec, |a, b| oper.join(a, b))
+}
+
+#[inline(always)]
+fn bitwise(out_vec: &mut [uint],
+           in_vec: &[uint],
+           op: &fn(uint, uint) -> uint) -> bool {
+    assert_eq!(out_vec.len(), in_vec.len());
+    let mut changed = false;
+    for uint::range(0, out_vec.len()) |i| {
+        let old_val = out_vec[i];
+        let new_val = op(old_val, in_vec[i]);
+        out_vec[i] = new_val;
+        changed |= (old_val != new_val);
+    }
+    return changed;
+}
+
+fn set_bit(words: &mut [uint], bit: uint) -> bool {
+    debug!("set_bit: words=%s bit=%s",
+           mut_bits_to_str(words), bit_str(bit));
+    let word = bit / uint::bits;
+    let bit_in_word = bit % uint::bits;
+    let bit_mask = 1 << bit_in_word;
+    debug!("word=%u bit_in_word=%u bit_mask=%u", word, bit_in_word, word);
+    let oldv = words[word];
+    let newv = oldv | bit_mask;
+    words[word] = newv;
+    oldv != newv
+}
+
+fn bit_str(bit: uint) -> ~str {
+    let byte = bit >> 8;
+    let lobits = 1 << (bit & 0xFF);
+    fmt!("[%u:%u-%02x]", bit, byte, lobits)
+}
+
+fn reslice<'a>(v: &'a mut [uint]) -> &'a [uint] {
+    // bFIXME(#5074) this function should not be necessary at all
+    unsafe {
+        cast::transmute(v)
+    }
+}
+
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index cf488b0ac89..199eb274ab9 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -10,7 +10,6 @@
 
 use middle::freevars::freevar_entry;
 use middle::freevars;
-use middle::liveness;
 use middle::pat_util;
 use middle::ty;
 use middle::typeck;
@@ -56,19 +55,16 @@ pub static try_adding: &'static str = "Try adding a move";
 pub struct Context {
     tcx: ty::ctxt,
     method_map: typeck::method_map,
-    last_use_map: liveness::last_use_map,
-    current_item: node_id,
+    current_item: node_id
 }
 
 pub fn check_crate(tcx: ty::ctxt,
                    method_map: typeck::method_map,
-                   last_use_map: liveness::last_use_map,
                    crate: @crate) {
     let ctx = Context {
         tcx: tcx,
         method_map: method_map,
-        last_use_map: last_use_map,
-        current_item: -1,
+        current_item: -1
     };
     let visit = visit::mk_vt(@visit::Visitor {
         visit_arm: check_arm,
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index 2de12b9eb97..784db49a0fd 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -252,10 +252,9 @@ pub impl LanguageItems {
     }
 }
 
-fn LanguageItemCollector<'r>(crate: @crate,
-                             session: Session,
-                             items: &'r mut LanguageItems)
-                          -> LanguageItemCollector<'r> {
+fn LanguageItemCollector(crate: @crate,
+                         session: Session)
+                      -> LanguageItemCollector {
     let mut item_refs = HashMap::new();
 
     item_refs.insert(@~"const", ConstTraitLangItem as uint);
@@ -303,13 +302,13 @@ fn LanguageItemCollector<'r>(crate: @crate,
     LanguageItemCollector {
         crate: crate,
         session: session,
-        items: items,
+        items: LanguageItems::new(),
         item_refs: item_refs
     }
 }
 
-struct LanguageItemCollector<'self> {
-    items: &'self mut LanguageItems,
+struct LanguageItemCollector {
+    items: LanguageItems,
 
     crate: @crate,
     session: Session,
@@ -317,8 +316,8 @@ struct LanguageItemCollector<'self> {
     item_refs: HashMap<@~str, uint>,
 }
 
-pub impl<'self> LanguageItemCollector<'self> {
-    fn match_and_collect_meta_item(&self, item_def_id: def_id,
+pub impl LanguageItemCollector {
+    fn match_and_collect_meta_item(&mut self, item_def_id: def_id,
                                    meta_item: @meta_item) {
         match meta_item.node {
             meta_name_value(key, literal) => {
@@ -333,7 +332,7 @@ pub impl<'self> LanguageItemCollector<'self> {
         }
     }
 
-    fn collect_item(&self, item_index: uint, item_def_id: def_id) {
+    fn collect_item(&mut self, item_index: uint, item_def_id: def_id) {
         // Check for duplicates.
         match self.items.items[item_index] {
             Some(original_def_id) if original_def_id != item_def_id => {
@@ -349,34 +348,37 @@ pub impl<'self> LanguageItemCollector<'self> {
         self.items.items[item_index] = Some(item_def_id);
     }
 
-    fn match_and_collect_item(&self,
+    fn match_and_collect_item(&mut self,
                               item_def_id: def_id, key: @~str, value: @~str) {
         if *key != ~"lang" {
             return;    // Didn't match.
         }
 
-        match self.item_refs.find(&value) {
+        let item_index = self.item_refs.find(&value).map(|x| **x);
+        // prevent borrow checker from considering   ^~~~~~~~~~~
+        // self to be borrowed (annoying)
+
+        match item_index {
+            Some(item_index) => {
+                self.collect_item(item_index, item_def_id);
+            }
             None => {
                 // Didn't match.
-            }
-            Some(&item_index) => {
-                self.collect_item(item_index, item_def_id)
+                return;
             }
         }
     }
 
-    fn collect_local_language_items(&self) {
-        unsafe {
-            let this: *LanguageItemCollector<'self> = transmute(self);
-            visit_crate(self.crate, (), mk_simple_visitor(@SimpleVisitor {
-                visit_item: |item| {
-                    for item.attrs.each |attribute| {
-                        unsafe {
-                            (*this).match_and_collect_meta_item(
-                                local_def(item.id),
-                                attribute.node.value
-                            );
-                        }
+    fn collect_local_language_items(&mut self) {
+        let this = ptr::addr_of(&self);
+        visit_crate(self.crate, (), mk_simple_visitor(@SimpleVisitor {
+            visit_item: |item| {
+                for item.attrs.each |attribute| {
+                    unsafe {
+                        (*this).match_and_collect_meta_item(
+                            local_def(item.id),
+                            attribute.node.value
+                        );
                     }
                 },
                 .. *default_simple_visitor()
@@ -384,7 +386,7 @@ pub impl<'self> LanguageItemCollector<'self> {
         }
     }
 
-    fn collect_external_language_items(&self) {
+    fn collect_external_language_items(&mut self) {
         let crate_store = self.session.cstore;
         do iter_crate_data(crate_store) |crate_number, _crate_metadata| {
             for each_lang_item(crate_store, crate_number)
@@ -408,7 +410,7 @@ pub impl<'self> LanguageItemCollector<'self> {
         }
     }
 
-    fn collect(&self) {
+    fn collect(&mut self) {
         self.collect_local_language_items();
         self.collect_external_language_items();
         self.check_completeness();
@@ -418,9 +420,9 @@ pub impl<'self> LanguageItemCollector<'self> {
 pub fn collect_language_items(crate: @crate,
                               session: Session)
                            -> LanguageItems {
-    let mut items = LanguageItems::new();
-    let collector = LanguageItemCollector(crate, session, &mut items);
+    let mut collector = LanguageItemCollector(crate, session);
     collector.collect();
-    copy items
+    let LanguageItemCollector { items, _ } = collector;
+    items
 }
 
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 94d82d0acb8..2e2c92abcdc 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -121,16 +121,6 @@ use syntax::visit::{fk_anon, fk_dtor, fk_fn_block, fk_item_fn, fk_method};
 use syntax::visit::{vt};
 use syntax::{visit, ast_util};
 
-// Maps from an expr id to a list of variable ids for which this expr
-// is the last use.  Typically, the expr is a path and the node id is
-// the local/argument/etc that the path refers to.  However, it also
-// possible for the expr to be a closure, in which case the list is a
-// list of closed over variables that can be moved into the closure.
-//
-// Very subtle (#2633): borrowck will remove entries from this table
-// if it detects an outstanding loan (that is, the addr is taken).
-pub type last_use_map = @mut HashMap<node_id, @mut ~[node_id]>;
-
 #[deriving(Eq)]
 struct Variable(uint);
 #[deriving(Eq)]
@@ -158,7 +148,7 @@ pub fn check_crate(tcx: ty::ctxt,
                    method_map: typeck::method_map,
                    variable_moves_map: moves::VariableMovesMap,
                    capture_map: moves::CaptureMap,
-                   crate: @crate) -> last_use_map {
+                   crate: @crate) {
     let visitor = visit::mk_vt(@visit::Visitor {
         visit_fn: visit_fn,
         visit_local: visit_local,
@@ -168,16 +158,13 @@ pub fn check_crate(tcx: ty::ctxt,
         .. *visit::default_visitor()
     });
 
-    let last_use_map = @mut HashMap::new();
     let initial_maps = @mut IrMaps(tcx,
                                    method_map,
                                    variable_moves_map,
                                    capture_map,
-                                   last_use_map,
                                    0);
     visit::visit_crate(crate, initial_maps, visitor);
     tcx.sess.abort_if_errors();
-    return last_use_map;
 }
 
 impl to_str::ToStr for LiveNode {
@@ -241,23 +228,11 @@ enum VarKind {
     ImplicitRet
 }
 
-fn relevant_def(def: def) -> Option<node_id> {
-    match def {
-        def_binding(nid, _) |
-        def_arg(nid, _) |
-        def_local(nid, _) |
-        def_self(nid, _) => Some(nid),
-
-        _ => None
-    }
-}
-
 struct IrMaps {
     tcx: ty::ctxt,
     method_map: typeck::method_map,
     variable_moves_map: moves::VariableMovesMap,
     capture_map: moves::CaptureMap,
-    last_use_map: last_use_map,
 
     num_live_nodes: uint,
     num_vars: uint,
@@ -274,7 +249,6 @@ fn IrMaps(tcx: ty::ctxt,
           method_map: typeck::method_map,
           variable_moves_map: moves::VariableMovesMap,
           capture_map: moves::CaptureMap,
-          last_use_map: last_use_map,
           cur_item: node_id)
        -> IrMaps {
     IrMaps {
@@ -282,7 +256,6 @@ fn IrMaps(tcx: ty::ctxt,
         method_map: method_map,
         variable_moves_map: variable_moves_map,
         capture_map: capture_map,
-        last_use_map: last_use_map,
         num_live_nodes: 0,
         num_vars: 0,
         live_node_map: HashMap::new(),
@@ -367,29 +340,6 @@ pub impl IrMaps {
     fn lnk(&mut self, ln: LiveNode) -> LiveNodeKind {
         self.lnks[*ln]
     }
-
-    fn add_last_use(&mut self, expr_id: node_id, var: Variable) {
-        let vk = self.var_kinds[*var];
-        debug!("Node %d is a last use of variable %?", expr_id, vk);
-        match vk {
-            Arg(id, _) |
-            Local(LocalInfo { id: id, kind: FromLetNoInitializer, _ }) |
-            Local(LocalInfo { id: id, kind: FromLetWithInitializer, _ }) |
-            Local(LocalInfo { id: id, kind: FromMatch(_), _ }) => {
-                let v = match self.last_use_map.find(&expr_id) {
-                    Some(&v) => v,
-                    None => {
-                        let v = @mut ~[];
-                        self.last_use_map.insert(expr_id, v);
-                        v
-                    }
-                };
-
-                v.push(id);
-            }
-            ImplicitRet => debug!("--but it is not owned"),
-        }
-    }
 }
 
 fn visit_item(item: @item, self: @mut IrMaps, v: vt<@mut IrMaps>) {
@@ -413,7 +363,6 @@ fn visit_fn(fk: &visit::fn_kind,
                               self.method_map,
                               self.variable_moves_map,
                               self.capture_map,
-                              self.last_use_map,
                               self.cur_item);
 
     unsafe {
@@ -522,7 +471,7 @@ fn visit_expr(expr: @expr, self: @mut IrMaps, vt: vt<@mut IrMaps>) {
       expr_path(_) => {
         let def = *self.tcx.def_map.get(&expr.id);
         debug!("expr %d: path that leads to %?", expr.id, def);
-        if relevant_def(def).is_some() {
+        if moves::moved_variable_node_id_from_def(def).is_some() {
             self.add_live_node_for_node(expr.id, ExprNode(expr.span));
         }
         visit::visit_expr(expr, self, vt);
@@ -539,7 +488,7 @@ fn visit_expr(expr: @expr, self: @mut IrMaps, vt: vt<@mut IrMaps>) {
         let cvs = self.capture_map.get(&expr.id);
         let mut call_caps = ~[];
         for cvs.each |cv| {
-            match relevant_def(cv.def) {
+            match moves::moved_variable_node_id_from_def(cv.def) {
               Some(rv) => {
                 let cv_ln = self.add_live_node(FreeVarNode(cv.span));
                 let is_move = match cv.mode {
@@ -668,7 +617,7 @@ pub impl Liveness {
         match expr.node {
           expr_path(_) => {
             let def = *self.tcx.def_map.get(&expr.id);
-            relevant_def(def).map(
+            moves::moved_variable_node_id_from_def(def).map(
                 |rdef| self.variable(*rdef, expr.span)
             )
           }
@@ -684,7 +633,7 @@ pub impl Liveness {
                              span: span) -> Option<Variable> {
         match self.tcx.def_map.find(&node_id) {
           Some(&def) => {
-            relevant_def(def).map(
+            moves::moved_variable_node_id_from_def(def).map(
                 |rdef| self.variable(*rdef, span)
             )
           }
@@ -1388,7 +1337,7 @@ pub impl Liveness {
     fn access_path(&self, expr: @expr, succ: LiveNode, acc: uint)
                   -> LiveNode {
         let def = *self.tcx.def_map.get(&expr.id);
-        match relevant_def(def) {
+        match moves::moved_variable_node_id_from_def(def) {
           Some(nid) => {
             let ln = self.live_node(expr.id, expr.span);
             if acc != 0u {
@@ -1521,7 +1470,6 @@ fn check_expr(expr: @expr, self: @Liveness, vt: vt<@Liveness>) {
       expr_path(_) => {
         for self.variable_from_def_map(expr.id, expr.span).each |var| {
             let ln = self.live_node(expr.id, expr.span);
-            self.consider_last_use(expr, ln, *var);
 
             match self.ir.variable_moves_map.find(&expr.id) {
                 None => {}
@@ -1540,7 +1488,6 @@ fn check_expr(expr: @expr, self: @Liveness, vt: vt<@Liveness>) {
         let caps = self.ir.captures(expr);
         for caps.each |cap| {
             let var = self.variable(cap.var_nid, expr.span);
-            self.consider_last_use(expr, cap.ln, var);
             if cap.is_move {
                 self.check_move_from_var(cap.ln, var, expr);
             }
@@ -1609,7 +1556,7 @@ enum ReadKind {
 }
 
 pub impl Liveness {
-    fn check_ret(@self, id: node_id, sp: span, _fk: &visit::fn_kind,
+    fn check_ret(&self, id: node_id, sp: span, _fk: &visit::fn_kind,
                  entry_ln: LiveNode) {
         if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
             // if no_ret_var is live, then we fall off the end of the
@@ -1629,11 +1576,11 @@ pub impl Liveness {
         }
     }
 
-    fn check_move_from_var(@self, ln: LiveNode,
+    fn check_move_from_var(&self,
+                           ln: LiveNode,
                            var: Variable,
                            move_expr: @expr) {
         /*!
-         *
          * Checks whether `var` is live on entry to any of the
          * successors of `ln`.  If it is, report an error.
          * `move_expr` is the expression which caused the variable
@@ -1653,16 +1600,6 @@ pub impl Liveness {
         }
     }
 
-    fn consider_last_use(@self, expr: @expr, ln: LiveNode, var: Variable) {
-        debug!("consider_last_use(expr.id=%?, ln=%s, var=%s)",
-               expr.id, ln.to_str(), var.to_str());
-
-        match self.live_on_exit(ln, var) {
-          Some(_) => {}
-          None => self.ir.add_last_use(expr.id, var)
-       }
-    }
-
     fn check_lvalue(@self, expr: @expr, vt: vt<@Liveness>) {
         match expr.node {
           expr_path(_) => {
@@ -1679,7 +1616,7 @@ pub impl Liveness {
                 self.warn_about_dead_assign(expr.span, expr.id, ln, var);
               }
               def => {
-                match relevant_def(def) {
+                match moves::moved_variable_node_id_from_def(def) {
                   Some(nid) => {
                     let ln = self.live_node(expr.id, expr.span);
                     let var = self.variable(nid, expr.span);
@@ -1699,14 +1636,14 @@ pub impl Liveness {
        }
     }
 
-    fn check_for_reassignments_in_pat(@self, pat: @pat, mutbl: bool) {
+    fn check_for_reassignments_in_pat(&self, pat: @pat, mutbl: bool) {
         do self.pat_bindings(pat) |ln, var, sp, id| {
             self.check_for_reassignment(ln, var, sp,
                                         if mutbl {Some(id)} else {None});
         }
     }
 
-    fn check_for_reassignment(@self, ln: LiveNode, var: Variable,
+    fn check_for_reassignment(&self, ln: LiveNode, var: Variable,
                               orig_span: span, mutbl: Option<node_id>) {
         match self.assigned_on_exit(ln, var) {
           Some(ExprNode(span)) => {
@@ -1731,7 +1668,7 @@ pub impl Liveness {
         }
     }
 
-    fn report_illegal_move(@self, lnk: LiveNodeKind,
+    fn report_illegal_move(&self, lnk: LiveNodeKind,
                            var: Variable,
                            move_expr: @expr) {
         // the only time that it is possible to have a moved variable
@@ -1796,7 +1733,8 @@ pub impl Liveness {
         };
     }
 
-    fn report_move_location(@self, move_expr: @expr,
+    fn report_move_location(&self,
+                            move_expr: @expr,
                             var: Variable,
                             expr_descr: &str,
                             pronoun: &str) {
@@ -1810,7 +1748,8 @@ pub impl Liveness {
                  ty_to_str(self.tcx, move_expr_ty)));
     }
 
-    fn report_illegal_read(@self, chk_span: span,
+    fn report_illegal_read(&self,
+                           chk_span: span,
                            lnk: LiveNodeKind,
                            var: Variable,
                            rk: ReadKind) {
@@ -1841,12 +1780,12 @@ pub impl Liveness {
         }
     }
 
-    fn should_warn(@self, var: Variable) -> Option<@~str> {
+    fn should_warn(&self, var: Variable) -> Option<@~str> {
         let name = self.ir.variable_name(var);
         if name[0] == ('_' as u8) { None } else { Some(name) }
     }
 
-    fn warn_about_unused_args(@self, decl: &fn_decl, entry_ln: LiveNode) {
+    fn warn_about_unused_args(&self, decl: &fn_decl, entry_ln: LiveNode) {
         for decl.inputs.each |arg| {
             do pat_util::pat_bindings(self.tcx.def_map, arg.pat)
                     |_bm, p_id, sp, _n| {
@@ -1856,7 +1795,7 @@ pub impl Liveness {
         }
     }
 
-    fn warn_about_unused_or_dead_vars_in_pat(@self, pat: @pat) {
+    fn warn_about_unused_or_dead_vars_in_pat(&self, pat: @pat) {
         do self.pat_bindings(pat) |ln, var, sp, id| {
             if !self.warn_about_unused(sp, id, ln, var) {
                 self.warn_about_dead_assign(sp, id, ln, var);
@@ -1864,7 +1803,7 @@ pub impl Liveness {
         }
     }
 
-    fn warn_about_unused(@self, sp: span, id: node_id,
+    fn warn_about_unused(&self, sp: span, id: node_id,
                          ln: LiveNode, var: Variable) -> bool {
         if !self.used_on_entry(ln, var) {
             for self.should_warn(var).each |name| {
@@ -1894,7 +1833,7 @@ pub impl Liveness {
         return false;
     }
 
-    fn warn_about_dead_assign(@self, sp: span, id: node_id,
+    fn warn_about_dead_assign(&self, sp: span, id: node_id,
                               ln: LiveNode, var: Variable) {
         if self.live_on_exit(ln, var).is_none() {
             for self.should_warn(var).each |name| {
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 7fa198be1d4..f525230664a 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -48,7 +48,7 @@
 
 use middle::ty;
 use middle::typeck;
-use util::ppaux::{ty_to_str, region_to_str};
+use util::ppaux::{ty_to_str, region_to_str, Repr};
 use util::common::indenter;
 
 use syntax::ast::{m_imm, m_const, m_mutbl};
@@ -58,50 +58,48 @@ use syntax::print::pprust;
 
 #[deriving(Eq)]
 pub enum categorization {
-    cat_rvalue,                     // result of eval'ing some misc expr
-    cat_special(special_kind),      //
-    cat_local(ast::node_id),        // local variable
-    cat_binding(ast::node_id),      // pattern binding
-    cat_arg(ast::node_id),          // formal argument
-    cat_stack_upvar(cmt),           // upvar in stack closure
-    cat_deref(cmt, uint, ptr_kind), // deref of a ptr
-    cat_comp(cmt, comp_kind),       // adjust to locate an internal component
-    cat_discr(cmt, ast::node_id),   // match discriminant (see preserve())
-    cat_self(ast::node_id),         // explicit `self`
+    cat_rvalue,                        // result of eval'ing some misc expr
+    cat_static_item,
+    cat_implicit_self,
+    cat_copied_upvar(CopiedUpvar),     // upvar copied into @fn or ~fn env
+    cat_stack_upvar(cmt),              // by ref upvar from &fn
+    cat_local(ast::node_id),           // local variable
+    cat_arg(ast::node_id, ast::rmode), // formal argument
+    cat_deref(cmt, uint, ptr_kind),    // deref of a ptr
+    cat_interior(cmt, interior_kind),          // something interior
+    cat_discr(cmt, ast::node_id),      // match discriminant (see preserve())
+    cat_self(ast::node_id),            // explicit `self`
+}
+
+#[deriving(Eq)]
+struct CopiedUpvar {
+    upvar_id: ast::node_id,
+    onceness: ast::Onceness,
 }
 
 // different kinds of pointers:
 #[deriving(Eq)]
 pub enum ptr_kind {
-    uniq_ptr,
+    uniq_ptr(ast::mutability),
     gc_ptr(ast::mutability),
     region_ptr(ast::mutability, ty::Region),
     unsafe_ptr
 }
 
-// I am coining the term "components" to mean "pieces of a data
-// structure accessible without a dereference":
+// We use the term "interior" to mean "something reachable from the
+// base without a pointer dereference", e.g. a field
 #[deriving(Eq)]
-pub enum comp_kind {
-    comp_tuple,                  // elt in a tuple
-    comp_anon_field,             // anonymous field (in e.g.
+pub enum interior_kind {
+    interior_tuple,                  // elt in a tuple
+    interior_anon_field,             // anonymous field (in e.g.
                                  // struct Foo(int, int);
-    comp_variant(ast::def_id),   // internals to a variant of given enum
-    comp_field(ast::ident,       // name of field
+    interior_variant(ast::def_id),   // internals to a variant of given enum
+    interior_field(ast::ident,       // name of field
                ast::mutability), // declared mutability of field
-    comp_index(ty::t,            // type of vec/str/etc being deref'd
+    interior_index(ty::t,            // type of vec/str/etc being deref'd
                ast::mutability)  // mutability of vec content
 }
 
-// different kinds of expressions we might evaluate
-#[deriving(Eq)]
-pub enum special_kind {
-    sk_method,
-    sk_static_item,
-    sk_implicit_self,   // old by-reference `self`
-    sk_heap_upvar
-}
-
 #[deriving(Eq)]
 pub enum MutabilityCategory {
     McImmutable, // Immutable.
@@ -120,39 +118,29 @@ pub struct cmt_ {
     id: ast::node_id,          // id of expr/pat producing this value
     span: span,                // span of same expr/pat
     cat: categorization,       // categorization of expr
-    lp: Option<@loan_path>,    // loan path for expr, if any
     mutbl: MutabilityCategory, // mutability of expr as lvalue
     ty: ty::t                  // type of the expr
 }
 
 pub type cmt = @cmt_;
 
-// a loan path is like a category, but it exists only when the data is
-// interior to the stack frame.  loan paths are used as the key to a
-// map indicating what is borrowed at any point in time.
-#[deriving(Eq)]
-pub enum loan_path {
-    lp_local(ast::node_id),
-    lp_arg(ast::node_id),
-    lp_self,
-    lp_deref(@loan_path, ptr_kind),
-    lp_comp(@loan_path, comp_kind)
-}
-
 // We pun on *T to mean both actual deref of a ptr as well
 // as accessing of components:
-pub enum deref_kind {deref_ptr(ptr_kind), deref_comp(comp_kind)}
+pub enum deref_kind {deref_ptr(ptr_kind), deref_interior(interior_kind)}
 
 // Categorizes a derefable type.  Note that we include vectors and strings as
 // derefable (we model an index as the combination of a deref and then a
 // pointer adjustment).
 pub fn opt_deref_kind(t: ty::t) -> Option<deref_kind> {
     match ty::get(t).sty {
-        ty::ty_uniq(*) |
+        ty::ty_uniq(mt) => {
+            Some(deref_ptr(uniq_ptr(mt.mutbl)))
+        }
+
         ty::ty_evec(_, ty::vstore_uniq) |
         ty::ty_estr(ty::vstore_uniq) |
         ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, _}) => {
-            Some(deref_ptr(uniq_ptr))
+            Some(deref_ptr(uniq_ptr(m_imm)))
         }
 
         ty::ty_rptr(r, mt) |
@@ -181,19 +169,19 @@ pub fn opt_deref_kind(t: ty::t) -> Option<deref_kind> {
         }
 
         ty::ty_enum(did, _) => {
-            Some(deref_comp(comp_variant(did)))
+            Some(deref_interior(interior_variant(did)))
         }
 
         ty::ty_struct(_, _) => {
-            Some(deref_comp(comp_anon_field))
+            Some(deref_interior(interior_anon_field))
         }
 
         ty::ty_evec(mt, ty::vstore_fixed(_)) => {
-            Some(deref_comp(comp_index(t, mt.mutbl)))
+            Some(deref_interior(interior_index(t, mt.mutbl)))
         }
 
         ty::ty_estr(ty::vstore_fixed(_)) => {
-            Some(deref_comp(comp_index(t, m_imm)))
+            Some(deref_interior(interior_index(t, m_imm)))
         }
 
         _ => None
@@ -338,21 +326,11 @@ pub impl MutabilityCategory {
         }
     }
 
-    fn to_user_str(&self) -> ~str {
+    fn to_user_str(&self) -> &'static str {
         match *self {
-            McDeclared | McInherited => ~"mutable",
-            McImmutable => ~"immutable",
-            McReadOnly => ~"const"
-        }
-    }
-}
-
-pub impl loan_path {
-    fn node_id(&self) -> Option<ast::node_id> {
-        match *self {
-            lp_local(id) | lp_arg(id) => Some(id),
-            lp_deref(lp, _) | lp_comp(lp, _) => lp.node_id(),
-            lp_self => None
+            McDeclared | McInherited => "mutable",
+            McImmutable => "immutable",
+            McReadOnly => "const"
         }
     }
 }
@@ -419,9 +397,9 @@ pub impl mem_categorization_ctxt {
           }
 
           ast::expr_field(base, f_name, _) => {
-            if self.method_map.contains_key(&expr.id) {
-                return self.cat_method_ref(expr, expr_ty);
-            }
+            // Method calls are now a special syntactic form,
+            // so `a.b` should always be a field.
+            assert!(!self.method_map.contains_key(&expr.id));
 
             let base_cmt = self.cat_expr(base);
             self.cat_field(expr, base_cmt, f_name, expr.id)
@@ -475,8 +453,7 @@ pub impl mem_categorization_ctxt {
             @cmt_ {
                 id:id,
                 span:span,
-                cat:cat_special(sk_static_item),
-                lp:None,
+                cat:cat_static_item,
                 mutbl: McImmutable,
                 ty:expr_ty
             }
@@ -487,66 +464,71 @@ pub impl mem_categorization_ctxt {
             // stuff as `&const` and `&mut`?
 
             // m: mutability of the argument
-            // lp: loan path, must be none for aliasable things
             let m = if mutbl {McDeclared} else {McImmutable};
-            let lp = Some(@lp_arg(vid));
+            let mode = ty::resolved_mode(self.tcx, mode);
             @cmt_ {
-                id:id,
-                span:span,
-                cat:cat_arg(vid),
-                lp:lp,
+                id: id,
+                span: span,
+                cat: cat_arg(vid, mode),
                 mutbl: m,
                 ty:expr_ty
             }
           }
 
           ast::def_self(self_id, is_implicit) => {
-            let cat, loan_path;
-            if is_implicit {
-                cat = cat_special(sk_implicit_self);
-                loan_path = None;
+            let cat = if is_implicit {
+                cat_implicit_self
             } else {
-                cat = cat_self(self_id);
-                loan_path = Some(@lp_self);
+                cat_self(self_id)
             };
 
             @cmt_ {
                 id:id,
                 span:span,
                 cat:cat,
-                lp:loan_path,
                 mutbl: McImmutable,
                 ty:expr_ty
             }
           }
 
-          ast::def_upvar(_, inner, fn_node_id, _) => {
-            let ty = ty::node_id_to_type(self.tcx, fn_node_id);
-            let sigil = ty::ty_closure_sigil(ty);
-            match sigil {
-                ast::BorrowedSigil => {
-                    let upcmt = self.cat_def(id, span, expr_ty, *inner);
-                    @cmt_ {
-                        id:id,
-                        span:span,
-                        cat:cat_stack_upvar(upcmt),
-                        lp:upcmt.lp,
-                        mutbl:upcmt.mutbl,
-                        ty:upcmt.ty
-                    }
-                }
-                ast::OwnedSigil | ast::ManagedSigil => {
-                    // FIXME #2152 allow mutation of moved upvars
-                    @cmt_ {
-                        id:id,
-                        span:span,
-                        cat:cat_special(sk_heap_upvar),
-                        lp:None,
-                        mutbl:McImmutable,
-                        ty:expr_ty
-                    }
-                }
-            }
+          ast::def_upvar(upvar_id, inner, fn_node_id, _) => {
+              let ty = ty::node_id_to_type(self.tcx, fn_node_id);
+              match ty::get(ty).sty {
+                  ty::ty_closure(ref closure_ty) => {
+                      let sigil = closure_ty.sigil;
+                      match sigil {
+                          ast::BorrowedSigil => {
+                              let upvar_cmt =
+                                  self.cat_def(id, span, expr_ty, *inner);
+                              @cmt_ {
+                                  id:id,
+                                  span:span,
+                                  cat:cat_stack_upvar(upvar_cmt),
+                                  mutbl:upvar_cmt.mutbl.inherit(),
+                                  ty:upvar_cmt.ty
+                              }
+                          }
+                          ast::OwnedSigil | ast::ManagedSigil => {
+                              // FIXME #2152 allow mutation of moved upvars
+                              @cmt_ {
+                                  id:id,
+                                  span:span,
+                                  cat:cat_copied_upvar(CopiedUpvar {
+                                      upvar_id: upvar_id,
+                                      onceness: closure_ty.onceness}),
+                                  mutbl:McImmutable,
+                                  ty:expr_ty
+                              }
+                          }
+                      }
+                  }
+                  _ => {
+                      self.tcx.sess.span_bug(
+                          span,
+                          fmt!("Upvar of non-closure %? - %s",
+                               fn_node_id, ty.repr(self.tcx)));
+                  }
+              }
           }
 
           ast::def_local(vid, mutbl) => {
@@ -555,7 +537,6 @@ pub impl mem_categorization_ctxt {
                 id:id,
                 span:span,
                 cat:cat_local(vid),
-                lp:Some(@lp_local(vid)),
                 mutbl:m,
                 ty:expr_ty
             }
@@ -567,7 +548,6 @@ pub impl mem_categorization_ctxt {
                 id:id,
                 span:span,
                 cat:cat_local(vid),
-                lp:Some(@lp_local(vid)),
                 mutbl:McImmutable,
                 ty:expr_ty
             }
@@ -582,8 +562,7 @@ pub impl mem_categorization_ctxt {
         @cmt_ {
             id: arg.id(),
             span: arg.span(),
-            cat: cat_comp(cmt, comp_variant(enum_did)),
-            lp: cmt.lp.map(|l| @lp_comp(*l, comp_variant(enum_did)) ),
+            cat: cat_interior(cmt, interior_variant(enum_did)),
             mutbl: cmt.mutbl.inherit(),
             ty: self.tcx.ty(arg)
         }
@@ -594,7 +573,6 @@ pub impl mem_categorization_ctxt {
             id:elt.id(),
             span:elt.span(),
             cat:cat_rvalue,
-            lp:None,
             mutbl:McImmutable,
             ty:expr_ty
         }
@@ -606,9 +584,9 @@ pub impl mem_categorization_ctxt {
     /// or if the container is mutable.
     fn inherited_mutability(&self,
                             base_m: MutabilityCategory,
-                            comp_m: ast::mutability) -> MutabilityCategory
+                            interior_m: ast::mutability) -> MutabilityCategory
     {
-        match comp_m {
+        match interior_m {
             m_imm => base_m.inherit(),
             m_const => McReadOnly,
             m_mutbl => McDeclared
@@ -634,13 +612,11 @@ pub impl mem_categorization_ctxt {
             }
         };
         let m = self.inherited_mutability(base_cmt.mutbl, f_mutbl);
-        let f_comp = comp_field(f_name, f_mutbl);
-        let lp = base_cmt.lp.map(|lp| @lp_comp(*lp, f_comp) );
+        let f_interior = interior_field(f_name, f_mutbl);
         @cmt_ {
             id: node.id(),
             span: node.span(),
-            cat: cat_comp(base_cmt, f_comp),
-            lp:lp,
+            cat: cat_interior(base_cmt, f_interior),
             mutbl: m,
             ty: self.tcx.ty(node)
         }
@@ -688,25 +664,10 @@ pub impl mem_categorization_ctxt {
     {
         match deref_kind(self.tcx, base_cmt.ty) {
             deref_ptr(ptr) => {
-                let lp = do base_cmt.lp.chain_ref |l| {
-                    // Given that the ptr itself is loanable, we can
-                    // loan out deref'd uniq ptrs or mut ptrs as the data
-                    // they are the only way to mutably reach the data they
-                    // point at. Other ptr types admit mutable aliases and
-                    // are therefore not loanable.
-                    match ptr {
-                        uniq_ptr => Some(@lp_deref(*l, ptr)),
-                        region_ptr(ast::m_mutbl, _) => {
-                            Some(@lp_deref(*l, ptr))
-                        }
-                        gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => None
-                    }
-                };
-
                 // for unique ptrs, we inherit mutability from the
                 // owning reference.
                 let m = match ptr {
-                    uniq_ptr => {
+                    uniq_ptr(*) => {
                         self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
                     }
                     gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => {
@@ -718,20 +679,17 @@ pub impl mem_categorization_ctxt {
                     id:node.id(),
                     span:node.span(),
                     cat:cat_deref(base_cmt, deref_cnt, ptr),
-                    lp:lp,
                     mutbl:m,
                     ty:mt.ty
                 }
             }
 
-            deref_comp(comp) => {
-                let lp = base_cmt.lp.map(|l| @lp_comp(*l, comp) );
+            deref_interior(interior) => {
                 let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl);
                 @cmt_ {
                     id:node.id(),
                     span:node.span(),
-                    cat:cat_comp(base_cmt, comp),
-                    lp:lp,
+                    cat:cat_interior(base_cmt, interior),
                     mutbl:m,
                     ty:mt.ty
                 }
@@ -754,17 +712,10 @@ pub impl mem_categorization_ctxt {
 
         return match deref_kind(self.tcx, base_cmt.ty) {
           deref_ptr(ptr) => {
-            // (a) the contents are loanable if the base is loanable
-            // and this is a *unique* vector
-            let deref_lp = match ptr {
-              uniq_ptr => {base_cmt.lp.map(|lp| @lp_deref(*lp, uniq_ptr))}
-              _ => {None}
-            };
-
-            // (b) for unique ptrs, we inherit mutability from the
+            // for unique ptrs, we inherit mutability from the
             // owning reference.
             let m = match ptr {
-              uniq_ptr => {
+              uniq_ptr(*) => {
                 self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
               }
               gc_ptr(_) | region_ptr(_, _) | unsafe_ptr => {
@@ -772,37 +723,34 @@ pub impl mem_categorization_ctxt {
               }
             };
 
-            // (c) the deref is explicit in the resulting cmt
+            // the deref is explicit in the resulting cmt
             let deref_cmt = @cmt_ {
                 id:elt.id(),
                 span:elt.span(),
                 cat:cat_deref(base_cmt, 0u, ptr),
-                lp:deref_lp,
                 mutbl:m,
                 ty:mt.ty
             };
 
-            comp(elt, deref_cmt, base_cmt.ty, m, mt)
+            interior(elt, deref_cmt, base_cmt.ty, m, mt)
           }
 
-          deref_comp(_) => {
+          deref_interior(_) => {
             // fixed-length vectors have no deref
             let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl);
-            comp(elt, base_cmt, base_cmt.ty, m, mt)
+            interior(elt, base_cmt, base_cmt.ty, m, mt)
           }
         };
 
-        fn comp<N:ast_node>(elt: N, of_cmt: cmt,
-                             vect: ty::t, mutbl: MutabilityCategory,
-                             mt: ty::mt) -> cmt
+        fn interior<N: ast_node>(elt: N, of_cmt: cmt,
+                                 vect: ty::t, mutbl: MutabilityCategory,
+                                 mt: ty::mt) -> cmt
         {
-            let comp = comp_index(vect, mt.mutbl);
-            let index_lp = of_cmt.lp.map(|lp| @lp_comp(*lp, comp) );
+            let interior = interior_index(vect, mt.mutbl);
             @cmt_ {
                 id:elt.id(),
                 span:elt.span(),
-                cat:cat_comp(of_cmt, comp),
-                lp:index_lp,
+                cat:cat_interior(of_cmt, interior),
                 mutbl:mutbl,
                 ty:mt.ty
             }
@@ -815,8 +763,7 @@ pub impl mem_categorization_ctxt {
         @cmt_ {
             id: elt.id(),
             span: elt.span(),
-            cat: cat_comp(cmt, comp_tuple),
-            lp: cmt.lp.map(|l| @lp_comp(*l, comp_tuple) ),
+            cat: cat_interior(cmt, interior_tuple),
             mutbl: cmt.mutbl.inherit(),
             ty: self.tcx.ty(elt)
         }
@@ -828,26 +775,12 @@ pub impl mem_categorization_ctxt {
         @cmt_ {
             id: elt.id(),
             span: elt.span(),
-            cat: cat_comp(cmt, comp_anon_field),
-            lp: cmt.lp.map(|l| @lp_comp(*l, comp_anon_field)),
+            cat: cat_interior(cmt, interior_anon_field),
             mutbl: cmt.mutbl.inherit(),
             ty: self.tcx.ty(elt)
         }
     }
 
-    fn cat_method_ref(&self,
-                      expr: @ast::expr,
-                      expr_ty: ty::t) -> cmt {
-        @cmt_ {
-            id:expr.id,
-            span:expr.span,
-            cat:cat_special(sk_method),
-            lp:None,
-            mutbl:McImmutable,
-            ty:expr_ty
-        }
-    }
-
     fn cat_pattern(&self,
                    cmt: cmt,
                    pat: @ast::pat,
@@ -890,7 +823,7 @@ pub impl mem_categorization_ctxt {
         let tcx = self.tcx;
         debug!("cat_pattern: id=%d pat=%s cmt=%s",
                pat.id, pprust::pat_to_str(pat, tcx.sess.intr()),
-               self.cmt_to_repr(cmt));
+               cmt.repr(tcx));
         let _i = indenter();
 
         op(cmt, pat);
@@ -986,29 +919,6 @@ pub impl mem_categorization_ctxt {
         }
     }
 
-    fn cat_to_repr(&self, cat: categorization) -> ~str {
-        match cat {
-          cat_special(sk_method) => ~"method",
-          cat_special(sk_static_item) => ~"static_item",
-          cat_special(sk_implicit_self) => ~"implicit-self",
-          cat_special(sk_heap_upvar) => ~"heap-upvar",
-          cat_stack_upvar(_) => ~"stack-upvar",
-          cat_rvalue => ~"rvalue",
-          cat_local(node_id) => fmt!("local(%d)", node_id),
-          cat_binding(node_id) => fmt!("binding(%d)", node_id),
-          cat_arg(node_id) => fmt!("arg(%d)", node_id),
-          cat_self(node_id) => fmt!("self(%d)", node_id),
-          cat_deref(cmt, derefs, ptr) => {
-            fmt!("%s->(%s, %u)", self.cat_to_repr(cmt.cat),
-                 self.ptr_sigil(ptr), derefs)
-          }
-          cat_comp(cmt, comp) => {
-            fmt!("%s.%s", self.cat_to_repr(cmt.cat), *self.comp_to_repr(comp))
-          }
-          cat_discr(cmt, _) => self.cat_to_repr(cmt.cat)
-        }
-    }
-
     fn mut_to_str(&self, mutbl: ast::mutability) -> ~str {
         match mutbl {
           m_mutbl => ~"mutable",
@@ -1017,84 +927,33 @@ pub impl mem_categorization_ctxt {
         }
     }
 
-    fn ptr_sigil(&self, ptr: ptr_kind) -> ~str {
-        match ptr {
-          uniq_ptr => ~"~",
-          gc_ptr(_) => ~"@",
-          region_ptr(_, _) => ~"&",
-          unsafe_ptr => ~"*"
-        }
-    }
-
-    fn comp_to_repr(&self, comp: comp_kind) -> @~str {
-        match comp {
-          comp_field(fld, _) => self.tcx.sess.str_of(fld),
-          comp_index(*) => @~"[]",
-          comp_tuple => @~"()",
-          comp_anon_field => @~"<anonymous field>",
-          comp_variant(_) => @~"<enum>"
-        }
-    }
-
-    fn lp_to_str(&self, lp: @loan_path) -> ~str {
-        match *lp {
-          lp_local(node_id) => {
-            fmt!("local(%d)", node_id)
-          }
-          lp_arg(node_id) => {
-            fmt!("arg(%d)", node_id)
-          }
-          lp_self => ~"self",
-          lp_deref(lp, ptr) => {
-            fmt!("%s->(%s)", self.lp_to_str(lp),
-                 self.ptr_sigil(ptr))
-          }
-          lp_comp(lp, comp) => {
-            fmt!("%s.%s", self.lp_to_str(lp),
-                 *self.comp_to_repr(comp))
-          }
-        }
-    }
-
-    fn cmt_to_repr(&self, cmt: cmt) -> ~str {
-        fmt!("{%s id:%d m:%? lp:%s ty:%s}",
-             self.cat_to_repr(cmt.cat),
-             cmt.id,
-             cmt.mutbl,
-             cmt.lp.map_default(~"none", |p| self.lp_to_str(*p) ),
-             ty_to_str(self.tcx, cmt.ty))
-    }
-
     fn cmt_to_str(&self, cmt: cmt) -> ~str {
-        let mut_str = cmt.mutbl.to_user_str();
         match cmt.cat {
-          cat_special(sk_method) => ~"method",
-          cat_special(sk_static_item) => ~"static item",
-          cat_special(sk_implicit_self) => ~"self reference",
-          cat_special(sk_heap_upvar) => {
+          cat_static_item => ~"static item",
+          cat_implicit_self => ~"self reference",
+          cat_copied_upvar(_) => {
               ~"captured outer variable in a heap closure"
           }
           cat_rvalue => ~"non-lvalue",
-          cat_local(_) => mut_str + ~" local variable",
-          cat_binding(_) => ~"pattern binding",
+          cat_local(_) => ~"local variable",
           cat_self(_) => ~"self value",
-          cat_arg(_) => ~"argument",
-          cat_deref(_, _, pk) => fmt!("dereference of %s %s pointer",
-                                      mut_str, self.ptr_sigil(pk)),
-          cat_stack_upvar(_) => {
-            ~"captured outer " + mut_str + ~" variable in a stack closure"
-          }
-          cat_comp(_, comp_field(*)) => mut_str + ~" field",
-          cat_comp(_, comp_tuple) => ~"tuple content",
-          cat_comp(_, comp_anon_field) => ~"anonymous field",
-          cat_comp(_, comp_variant(_)) => ~"enum content",
-          cat_comp(_, comp_index(t, _)) => {
+          cat_arg(*) => ~"argument",
+          cat_deref(_, _, pk) => fmt!("dereference of %s pointer",
+                                      ptr_sigil(pk)),
+          cat_interior(_, interior_field(*)) => ~"field",
+          cat_interior(_, interior_tuple) => ~"tuple content",
+          cat_interior(_, interior_anon_field) => ~"anonymous field",
+          cat_interior(_, interior_variant(_)) => ~"enum content",
+          cat_interior(_, interior_index(t, _)) => {
             match ty::get(t).sty {
-              ty::ty_evec(*) => mut_str + ~" vec content",
-              ty::ty_estr(*) => mut_str + ~" str content",
-              _ => mut_str + ~" indexed content"
+              ty::ty_evec(*) => ~"vec content",
+              ty::ty_estr(*) => ~"str content",
+              _ => ~"indexed content"
             }
           }
+          cat_stack_upvar(_) => {
+              ~"captured outer variable"
+          }
           cat_discr(cmt, _) => {
             self.cmt_to_str(cmt)
           }
@@ -1149,33 +1008,142 @@ pub fn field_mutbl(tcx: ty::ctxt,
     return None;
 }
 
-pub impl categorization {
-    fn derefs_through_mutable_box(&const self) -> bool {
-        match *self {
-            cat_deref(_, _, gc_ptr(ast::m_mutbl)) => {
-                true
+pub enum AliasableReason {
+    AliasableManaged(ast::mutability),
+    AliasableBorrowed(ast::mutability),
+    AliasableOther
+}
+
+pub impl cmt_ {
+    fn guarantor(@self) -> cmt {
+        //! Returns `self` after stripping away any owned pointer derefs or
+        //! interior content. The return value is basically the `cmt` which
+        //! determines how long the value in `self` remains live.
+
+        match self.cat {
+            cat_rvalue |
+            cat_static_item |
+            cat_implicit_self |
+            cat_copied_upvar(*) |
+            cat_local(*) |
+            cat_self(*) |
+            cat_arg(*) |
+            cat_deref(_, _, unsafe_ptr(*)) |
+            cat_deref(_, _, gc_ptr(*)) |
+            cat_deref(_, _, region_ptr(*)) => {
+                self
             }
-            cat_deref(subcmt, _, _) |
-            cat_comp(subcmt, _) |
-            cat_discr(subcmt, _) |
-            cat_stack_upvar(subcmt) => {
-                subcmt.cat.derefs_through_mutable_box()
+            cat_stack_upvar(b) |
+            cat_discr(b, _) |
+            cat_interior(b, _) |
+            cat_deref(b, _, uniq_ptr(*)) => {
+                b.guarantor()
             }
+        }
+    }
+
+    fn is_freely_aliasable(&self) -> bool {
+        self.freely_aliasable().is_some()
+    }
+
+    fn freely_aliasable(&self) -> Option<AliasableReason> {
+        //! True if this lvalue resides in an area that is
+        //! freely aliasable, meaning that rustc cannot track
+        //! the alias//es with precision.
+
+        // Maybe non-obvious: copied upvars can only be considered
+        // non-aliasable in once closures, since any other kind can be
+        // aliased and eventually recused.
+
+        match self.cat {
+            cat_copied_upvar(CopiedUpvar {onceness: ast::Once, _}) |
+            cat_rvalue(*) |
+            cat_local(*) |
+            cat_arg(_, ast::by_copy) |
+            cat_self(*) |
+            cat_deref(_, _, unsafe_ptr(*)) | // of course it is aliasable, but...
+            cat_deref(_, _, region_ptr(m_mutbl, _)) => {
+                None
+            }
+
+            cat_copied_upvar(CopiedUpvar {onceness: ast::Many, _}) |
+            cat_static_item(*) |
+            cat_implicit_self(*) |
+            cat_arg(_, ast::by_ref) => {
+                Some(AliasableOther)
+            }
+
+            cat_deref(_, _, gc_ptr(m)) => {
+                Some(AliasableManaged(m))
+            }
+
+            cat_deref(_, _, region_ptr(m @ m_const, _)) |
+            cat_deref(_, _, region_ptr(m @ m_imm, _)) => {
+                Some(AliasableBorrowed(m))
+            }
+
+            cat_stack_upvar(b) |
+            cat_deref(b, _, uniq_ptr(*)) |
+            cat_interior(b, _) |
+            cat_discr(b, _) => {
+                b.freely_aliasable()
+            }
+        }
+    }
+}
+
+impl Repr for cmt {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        fmt!("{%s id:%d m:%? ty:%s}",
+             self.cat.repr(tcx),
+             self.id,
+             self.mutbl,
+             self.ty.repr(tcx))
+    }
+}
+
+impl Repr for categorization {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        match *self {
+            cat_static_item |
+            cat_implicit_self |
             cat_rvalue |
-            cat_special(*) |
+            cat_copied_upvar(*) |
             cat_local(*) |
-            cat_binding(*) |
-            cat_arg(*) |
-            cat_self(*) => {
-                false
+            cat_self(*) |
+            cat_arg(*) => fmt!("%?", *self),
+            cat_deref(cmt, derefs, ptr) => {
+                fmt!("%s->(%s, %u)", cmt.cat.repr(tcx),
+                     ptr_sigil(ptr), derefs)
+            }
+            cat_interior(cmt, interior) => {
+                fmt!("%s.%s",
+                     cmt.cat.repr(tcx),
+                     interior.repr(tcx))
             }
+            cat_stack_upvar(cmt) |
+            cat_discr(cmt, _) => cmt.cat.repr(tcx)
         }
     }
+}
+
+pub fn ptr_sigil(ptr: ptr_kind) -> ~str {
+    match ptr {
+        uniq_ptr(_) => ~"~",
+        gc_ptr(_) => ~"@",
+        region_ptr(_, _) => ~"&",
+        unsafe_ptr => ~"*"
+    }
+}
 
-    fn is_mutable_box(&const self) -> bool {
+impl Repr for interior_kind {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
         match *self {
-            cat_deref(_, _, gc_ptr(ast::m_mutbl)) => true,
-            _ => false
+            interior_field(fld, _) => copy *tcx.sess.str_of(fld),
+            interior_index(*) => ~"[]",
+            interior_tuple => ~"()",
+            interior_anon_field => ~"<anonymous field>",
+            interior_variant(_) => ~"<enum>"
         }
     }
 }
diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs
index fe1466bf808..d8a0e6bacf4 100644
--- a/src/librustc/middle/moves.rs
+++ b/src/librustc/middle/moves.rs
@@ -246,10 +246,19 @@ pub type MovesMap = @mut HashSet<node_id>;
  * expression */
 pub type VariableMovesMap = @mut HashMap<node_id, @expr>;
 
+/**
+ * Set of variable node-ids that are moved.
+ *
+ * Note: The `VariableMovesMap` stores expression ids that
+ * are moves, whereas this set stores the ids of the variables
+ * that are moved at some point */
+pub type MovedVariablesSet = @mut HashSet<node_id>;
+
 /** See the section Output on the module comment for explanation. */
 pub struct MoveMaps {
     moves_map: MovesMap,
     variable_moves_map: VariableMovesMap,
+    moved_variables_set: MovedVariablesSet,
     capture_map: CaptureMap
 }
 
@@ -279,13 +288,25 @@ pub fn compute_moves(tcx: ty::ctxt,
         move_maps: MoveMaps {
             moves_map: @mut HashSet::new(),
             variable_moves_map: @mut HashMap::new(),
-            capture_map: @mut HashMap::new()
+            capture_map: @mut HashMap::new(),
+            moved_variables_set: @mut HashSet::new()
         }
     };
     visit::visit_crate(crate, visit_cx, visitor);
     return visit_cx.move_maps;
 }
 
+pub fn moved_variable_node_id_from_def(def: def) -> Option<node_id> {
+    match def {
+      def_binding(nid, _) |
+      def_arg(nid, _, _) |
+      def_local(nid, _) |
+      def_self(nid, _) => Some(nid),
+
+      _ => None
+    }
+}
+
 // ______________________________________________________________________
 // Expressions
 
@@ -419,6 +440,11 @@ pub impl VisitContext {
                     MoveInPart(entire_expr) => {
                         self.move_maps.variable_moves_map.insert(
                             expr.id, entire_expr);
+
+                        let def = *self.tcx.def_map.get(&expr.id);
+                        for moved_variable_node_id_from_def(def).each |&id| {
+                            self.move_maps.moved_variables_set.insert(id);
+                        }
                     }
                     Read => {}
                     MoveInWhole => {
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index f3299828171..ea21ab0527b 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -47,59 +47,27 @@ The region maps encode information about region relationships.
   - the free region map is populated during type check as we check
     each function. See the function `relate_free_regions` for
     more information.
+- `cleanup_scopes` includes scopes where trans cleanups occur
+  - this is intended to reflect the current state of trans, not
+    necessarily how I think things ought to work
 */
 pub struct RegionMaps {
     priv scope_map: HashMap<ast::node_id, ast::node_id>,
     priv free_region_map: HashMap<FreeRegion, ~[FreeRegion]>,
+    priv cleanup_scopes: HashSet<ast::node_id>
 }
 
-pub struct ctxt {
+pub struct Context {
     sess: Session,
     def_map: resolve::DefMap,
 
     // Generated maps:
     region_maps: @mut RegionMaps,
 
-    // Generally speaking, expressions are parented to their innermost
-    // enclosing block. But some kinds of expressions serve as
-    // parents: calls, methods, etc.  In addition, some expressions
-    // serve as parents by virtue of where they appear.  For example,
-    // the condition in a while loop is always a parent.  In those
-    // cases, we add the node id of such an expression to this set so
-    // that when we visit it we can view it as a parent.
-    root_exprs: @mut HashSet<ast::node_id>,
-
-    // The parent scope is the innermost block, statement, call, or match
-    // expression during the execution of which the current expression
-    // will be evaluated.  Generally speaking, the innermost parent
-    // scope is also the closest suitable ancestor in the AST tree.
-    //
-    // There is a subtle point concerning call arguments.  Imagine
-    // you have a call:
-    //
-    // { // block a
-    //     foo( // call b
-    //        x,
-    //        y);
-    // }
-    //
-    // In what lifetime are the expressions `x` and `y` evaluated?  At
-    // first, I imagine the answer was the block `a`, as the arguments
-    // are evaluated before the call takes place.  But this turns out
-    // to be wrong.  The lifetime of the call must encompass the
-    // argument evaluation as well.
-    //
-    // The reason is that evaluation of an earlier argument could
-    // create a borrow which exists during the evaluation of later
-    // arguments.  Consider this torture test, for example,
-    //
-    // fn test1(x: @mut ~int) {
-    //     foo(&**x, *x = ~5);
-    // }
-    //
-    // Here, the first argument `&**x` will be a borrow of the `~int`,
-    // but the second argument overwrites that very value! Bad.
-    // (This test is borrowck-pure-scope-in-call.rs, btw)
+    // Scope where variables should be parented to
+    var_parent: parent,
+
+    // Innermost enclosing expression
     parent: parent,
 }
 
@@ -128,10 +96,22 @@ pub impl RegionMaps {
                      sup: ast::node_id)
     {
         debug!("record_parent(sub=%?, sup=%?)", sub, sup);
+        assert!(sub != sup);
 
         self.scope_map.insert(sub, sup);
     }
 
+    pub fn record_cleanup_scope(&mut self,
+                                scope_id: ast::node_id)
+    {
+        //! Records that a scope is a CLEANUP SCOPE.  This is invoked
+        //! from within regionck.  We wait until regionck because we do
+        //! not know which operators are overloaded until that point,
+        //! and only overloaded operators result in cleanup scopes.
+
+        self.cleanup_scopes.insert(scope_id);
+    }
+
     fn opt_encl_scope(&self,
                       id: ast::node_id) -> Option<ast::node_id>
     {
@@ -151,6 +131,22 @@ pub impl RegionMaps {
         }
     }
 
+    fn is_cleanup_scope(&self, scope_id: ast::node_id) -> bool {
+        self.cleanup_scopes.contains(&scope_id)
+    }
+
+    fn cleanup_scope(&self,
+                     expr_id: ast::node_id) -> ast::node_id
+    {
+        //! Returns the scope when temps in expr will be cleaned up
+
+        let mut id = self.encl_scope(expr_id);
+        while !self.cleanup_scopes.contains(&id) {
+            id = self.encl_scope(id);
+        }
+        return id;
+    }
+
     fn encl_region(&self,
                    id: ast::node_id) -> ty::Region
     {
@@ -159,22 +155,38 @@ pub impl RegionMaps {
         ty::re_scope(self.encl_scope(id))
     }
 
-    fn is_sub_scope(&self,
-                    sub_scope: ast::node_id,
-                    superscope: ast::node_id) -> bool
+    pub fn scopes_intersect(&self,
+                            scope1: ast::node_id,
+                            scope2: ast::node_id) -> bool
+    {
+        self.is_subscope_of(scope1, scope2) || self.is_subscope_of(scope2, scope1)
+    }
+
+    fn is_subscope_of(&self,
+                      subscope: ast::node_id,
+                      superscope: ast::node_id) -> bool
     {
         /*!
-         * Returns true if `sub_scope` is equal to or is lexically
+         * Returns true if `subscope` is equal to or is lexically
          * nested inside `superscope` and false otherwise.
          */
 
-        let mut sub_scope = sub_scope;
-        while superscope != sub_scope {
-            match self.scope_map.find(&sub_scope) {
-                None => return false,
-                Some(&scope) => sub_scope = scope
+        let mut s = subscope;
+        while superscope != s {
+            match self.scope_map.find(&s) {
+                None => {
+                    debug!("is_subscope_of(%?, %?, s=%?)=false",
+                           subscope, superscope, s);
+
+                    return false;
+                }
+                Some(&scope) => s = scope
             }
         }
+
+        debug!("is_subscope_of(%?, %?)=true",
+               subscope, superscope);
+
         return true;
     }
 
@@ -239,11 +251,11 @@ pub impl RegionMaps {
                 }
 
                 (ty::re_scope(sub_scope), ty::re_scope(super_scope)) => {
-                    self.is_sub_scope(sub_scope, super_scope)
+                    self.is_subscope_of(sub_scope, super_scope)
                 }
 
                 (ty::re_scope(sub_scope), ty::re_free(ref fr)) => {
-                    self.is_sub_scope(sub_scope, fr.scope_id)
+                    self.is_subscope_of(sub_scope, fr.scope_id)
                 }
 
                 (ty::re_free(sub_fr), ty::re_free(super_fr)) => {
@@ -301,6 +313,7 @@ pub impl RegionMaps {
         fn ancestors_of(self: &RegionMaps, scope: ast::node_id)
             -> ~[ast::node_id]
         {
+            // debug!("ancestors_of(scope=%d)", scope);
             let mut result = ~[scope];
             let mut scope = scope;
             loop {
@@ -311,13 +324,14 @@ pub impl RegionMaps {
                         scope = superscope;
                     }
                 }
+                // debug!("ancestors_of_loop(scope=%d)", scope);
             }
         }
     }
 }
 
 /// Extracts that current parent from cx, failing if there is none.
-pub fn parent_id(cx: ctxt, span: span) -> ast::node_id {
+pub fn parent_id(cx: Context, span: span) -> ast::node_id {
     match cx.parent {
       None => {
         cx.sess.span_bug(span, ~"crate should not be parent here");
@@ -329,144 +343,137 @@ pub fn parent_id(cx: ctxt, span: span) -> ast::node_id {
 }
 
 /// Records the current parent (if any) as the parent of `child_id`.
-pub fn record_parent(cx: ctxt, child_id: ast::node_id) {
+pub fn parent_to_expr(cx: Context, child_id: ast::node_id) {
     for cx.parent.each |parent_id| {
         cx.region_maps.record_parent(child_id, *parent_id);
     }
 }
 
-pub fn resolve_block(blk: &ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
+pub fn resolve_block(blk: &ast::blk, cx: Context, visitor: visit::vt<Context>) {
     // Record the parent of this block.
-    record_parent(cx, blk.node.id);
+    parent_to_expr(cx, blk.node.id);
 
     // Descend.
-    let new_cx: ctxt = ctxt {parent: Some(blk.node.id),.. cx};
+    let new_cx = Context {var_parent: Some(blk.node.id),
+                          parent: Some(blk.node.id),
+                          ..cx};
     visit::visit_block(blk, new_cx, visitor);
 }
 
-pub fn resolve_arm(arm: &ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
+pub fn resolve_arm(arm: &ast::arm, cx: Context, visitor: visit::vt<Context>) {
     visit::visit_arm(arm, cx, visitor);
 }
 
-pub fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
-    match pat.node {
-      ast::pat_ident(*) => {
-        let defn_opt = cx.def_map.find(&pat.id);
-        match defn_opt {
-          Some(&ast::def_variant(_,_)) => {
-            /* Nothing to do; this names a variant. */
-          }
-          _ => {
-            /* This names a local. Bind it to the containing scope. */
-            record_parent(cx, pat.id);
-          }
-        }
-      }
-      _ => { /* no-op */ }
-    }
-
+pub fn resolve_pat(pat: @ast::pat, cx: Context, visitor: visit::vt<Context>) {
+    assert!(cx.var_parent == cx.parent);
+    parent_to_expr(cx, pat.id);
     visit::visit_pat(pat, cx, visitor);
 }
 
-pub fn resolve_stmt(stmt: @ast::stmt, cx: ctxt, visitor: visit::vt<ctxt>) {
+pub fn resolve_stmt(stmt: @ast::stmt, cx: Context, visitor: visit::vt<Context>) {
     match stmt.node {
-      ast::stmt_decl(*) => {
-        visit::visit_stmt(stmt, cx, visitor);
-      }
-      // This code has to be kept consistent with trans::base::trans_stmt
-      ast::stmt_expr(_, stmt_id) |
-      ast::stmt_semi(_, stmt_id) => {
-        record_parent(cx, stmt_id);
-        let mut expr_cx = cx;
-        expr_cx.parent = Some(stmt_id);
-        visit::visit_stmt(stmt, expr_cx, visitor);
-      }
-      ast::stmt_mac(*) => cx.sess.bug(~"unexpanded macro")
+        ast::stmt_decl(*) => {
+            visit::visit_stmt(stmt, cx, visitor);
+        }
+        ast::stmt_expr(_, stmt_id) |
+        ast::stmt_semi(_, stmt_id) => {
+            parent_to_expr(cx, stmt_id);
+            let expr_cx = Context {parent: Some(stmt_id), ..cx};
+            visit::visit_stmt(stmt, expr_cx, visitor);
+        }
+        ast::stmt_mac(*) => cx.sess.bug(~"unexpanded macro")
     }
 }
 
-pub fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
-    record_parent(cx, expr.id);
+pub fn resolve_expr(expr: @ast::expr, cx: Context, visitor: visit::vt<Context>) {
+    parent_to_expr(cx, expr.id);
 
     let mut new_cx = cx;
+    new_cx.parent = Some(expr.id);
     match expr.node {
-      // Calls or overloadable operators
-      // FIXME #3387
-      // ast::expr_index(*) | ast::expr_binary(*) |
-      // ast::expr_unary(*) |
-      ast::expr_call(*) | ast::expr_method_call(*) => {
-        debug!("node %d: %s", expr.id, pprust::expr_to_str(expr,
-                                                           cx.sess.intr()));
-        new_cx.parent = Some(expr.id);
-      }
-      ast::expr_match(*) => {
-        debug!("node %d: %s", expr.id, pprust::expr_to_str(expr,
-                                                           cx.sess.intr()));
-        new_cx.parent = Some(expr.id);
-      }
-      ast::expr_while(cond, _) => {
-        new_cx.root_exprs.insert(cond.id);
-      }
-      _ => {}
+        ast::expr_assign_op(*) | ast::expr_index(*) | ast::expr_binary(*) |
+        ast::expr_unary(*) | ast::expr_call(*) | ast::expr_method_call(*) => {
+            // FIXME(#5074) Nested method calls
+            //
+            // The lifetimes for a call or method call look as follows:
+            //
+            // call.id
+            // - arg0.id
+            // - ...
+            // - argN.id
+            // - call.callee_id
+            //
+            // The idea is that call.callee_id represents *the time when
+            // the invoked function is actually running* and call.id
+            // represents *the time to prepare the arguments and make the
+            // call*.  See the section "Borrows in Calls" borrowck/doc.rs
+            // for an extended explanantion of why this distinction is
+            // important.
+            //
+            // parent_to_expr(new_cx, expr.callee_id);
+        }
+
+        ast::expr_match(*) => {
+            new_cx.var_parent = Some(expr.id);
+        }
+
+        _ => {}
     };
 
-    if new_cx.root_exprs.contains(&expr.id) {
-        new_cx.parent = Some(expr.id);
-    }
 
     visit::visit_expr(expr, new_cx, visitor);
 }
 
 pub fn resolve_local(local: @ast::local,
-                     cx: ctxt,
-                     visitor: visit::vt<ctxt>) {
-    record_parent(cx, local.node.id);
+                     cx: Context,
+                     visitor: visit::vt<Context>) {
+    assert!(cx.var_parent == cx.parent);
+    parent_to_expr(cx, local.node.id);
     visit::visit_local(local, cx, visitor);
 }
 
-pub fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
+pub fn resolve_item(item: @ast::item, cx: Context, visitor: visit::vt<Context>) {
     // Items create a new outer block scope as far as we're concerned.
-    let new_cx: ctxt = ctxt {parent: None,.. cx};
+    let new_cx = Context {var_parent: None, parent: None, ..cx};
     visit::visit_item(item, new_cx, visitor);
 }
 
 pub fn resolve_fn(fk: &visit::fn_kind,
                   decl: &ast::fn_decl,
                   body: &ast::blk,
-                  sp: span,
+                  _sp: span,
                   id: ast::node_id,
-                  cx: ctxt,
-                  visitor: visit::vt<ctxt>) {
-    let fn_cx = match *fk {
-        visit::fk_item_fn(*) | visit::fk_method(*) |
-        visit::fk_dtor(*) => {
-            // Top-level functions are a root scope.
-            ctxt {parent: Some(id),.. cx}
-        }
-
-        visit::fk_anon(*) | visit::fk_fn_block(*) => {
-            // Closures continue with the inherited scope.
-            cx
-        }
-    };
-
-    // Record the ID of `self`.
+                  cx: Context,
+                  visitor: visit::vt<Context>) {
+    debug!("region::resolve_fn(id=%?, body.node.id=%?, cx.parent=%?)",
+           id, body.node.id, cx.parent);
+
+    // The arguments and `self` are parented to the body of the fn.
+    let decl_cx = Context {parent: Some(body.node.id),
+                           var_parent: Some(body.node.id),
+                           ..cx};
     match *fk {
         visit::fk_method(_, _, method) => {
             cx.region_maps.record_parent(method.self_id, body.node.id);
         }
         _ => {}
     }
+    visit::visit_fn_decl(decl, decl_cx, visitor);
 
-    debug!("visiting fn with body %d. cx.parent: %? \
-            fn_cx.parent: %?",
-           body.node.id, cx.parent, fn_cx.parent);
-
-    for decl.inputs.each |input| {
-        cx.region_maps.record_parent(input.id, body.node.id);
-    }
-
-    visit::visit_fn(fk, decl, body, sp, id, fn_cx, visitor);
+    // The body of the fn itself is either a root scope (top-level fn)
+    // or it continues with the inherited scope (closures).
+    let body_cx = match *fk {
+        visit::fk_item_fn(*) |
+        visit::fk_method(*) |
+        visit::fk_dtor(*) => {
+            Context {parent: None, var_parent: None, ..cx}
+        }
+        visit::fk_anon(*) |
+        visit::fk_fn_block(*) => {
+            cx
+        }
+    };
+    (visitor.visit_block)(body, body_cx, visitor);
 }
 
 pub fn resolve_crate(sess: Session,
@@ -475,13 +482,14 @@ pub fn resolve_crate(sess: Session,
 {
     let region_maps = @mut RegionMaps {
         scope_map: HashMap::new(),
-        free_region_map: HashMap::new()
+        free_region_map: HashMap::new(),
+        cleanup_scopes: HashSet::new(),
     };
-    let cx: ctxt = ctxt {sess: sess,
-                         def_map: def_map,
-                         region_maps: region_maps,
-                         root_exprs: @mut HashSet::new(),
-                         parent: None};
+    let cx = Context {sess: sess,
+                      def_map: def_map,
+                      region_maps: region_maps,
+                      parent: None,
+                      var_parent: None};
     let visitor = visit::mk_vt(@visit::Visitor {
         visit_block: resolve_block,
         visit_item: resolve_item,
@@ -772,7 +780,8 @@ pub fn determine_rp_in_ty(ty: @ast::Ty,
                    pprust::ty_to_str(ty, sess.intr()));
 
             if cx.region_is_relevant(r) {
-                cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant))
+                let rv = cx.add_variance(rv_contravariant);
+                cx.add_rp(cx.item_id, rv)
             }
         }
 
@@ -782,14 +791,14 @@ pub fn determine_rp_in_ty(ty: @ast::Ty,
             match f.region {
                 Some(_) => {
                     if cx.region_is_relevant(f.region) {
-                        cx.add_rp(cx.item_id,
-                                  cx.add_variance(rv_contravariant))
+                        let rv = cx.add_variance(rv_contravariant);
+                        cx.add_rp(cx.item_id, rv)
                     }
                 }
                 None => {
                     if f.sigil == ast::BorrowedSigil && cx.anon_implies_rp {
-                        cx.add_rp(cx.item_id,
-                                  cx.add_variance(rv_contravariant));
+                        let rv = cx.add_variance(rv_contravariant);
+                        cx.add_rp(cx.item_id, rv)
                     }
                 }
             }
@@ -820,7 +829,8 @@ pub fn determine_rp_in_ty(ty: @ast::Ty,
                     debug!("reference to external, rp'd type %s",
                            pprust::ty_to_str(ty, sess.intr()));
                     if cx.region_is_relevant(path.rp) {
-                        cx.add_rp(cx.item_id, cx.add_variance(variance))
+                        let rv = cx.add_variance(variance);
+                        cx.add_rp(cx.item_id, rv)
                     }
                   }
                 }
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 294a21fac2c..ffc9d1488cf 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -971,7 +971,7 @@ pub impl Resolver {
                 module_.children.insert(name, child);
                 return (child, new_parent);
             }
-            Some(child) => {
+            Some(&child) => {
                 // Enforce the duplicate checking mode:
                 //
                 // * If we're requesting duplicate module checking, check that
@@ -1033,7 +1033,7 @@ pub impl Resolver {
                                   *self.session.str_of(name)));
                     }
                 }
-                return (*child, new_parent);
+                return (child, new_parent);
             }
         }
     }
@@ -1864,7 +1864,7 @@ pub impl Resolver {
                        *self.session.str_of(target));
 
                 match module_.import_resolutions.find(&target) {
-                    Some(resolution) => {
+                    Some(&resolution) => {
                         debug!("(building import directive) bumping \
                                 reference");
                         resolution.outstanding_references += 1;
@@ -2395,7 +2395,7 @@ pub impl Resolver {
                         (*ident, new_import_resolution);
                 }
                 None => { /* continue ... */ }
-                Some(dest_import_resolution) => {
+                Some(&dest_import_resolution) => {
                     // Merge the two import resolutions at a finer-grained
                     // level.
 
@@ -2433,8 +2433,8 @@ pub impl Resolver {
                     module_.import_resolutions.insert
                         (*ident, dest_import_resolution);
                 }
-                Some(existing_import_resolution) => {
-                    dest_import_resolution = *existing_import_resolution;
+                Some(&existing_import_resolution) => {
+                    dest_import_resolution = existing_import_resolution;
                 }
             }
 
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 3755cca8c35..785bb3edc07 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -1114,7 +1114,8 @@ pub fn compare_values(cx: block,
 pub fn store_non_ref_bindings(bcx: block,
                               data: &ArmData,
                               opt_temp_cleanups: Option<&mut ~[ValueRef]>)
-                           -> block {
+    -> block
+{
     /*!
      *
      * For each copy/move binding, copy the value from the value
@@ -1125,6 +1126,7 @@ pub fn store_non_ref_bindings(bcx: block,
      */
 
     let mut bcx = bcx;
+    let mut opt_temp_cleanups = opt_temp_cleanups;
     for data.bindings_map.each_value |&binding_info| {
         match binding_info.trmode {
             TrByValue(is_move, lldest) => {
@@ -1139,9 +1141,10 @@ pub fn store_non_ref_bindings(bcx: block,
                     }
                 };
 
-                for opt_temp_cleanups.each |temp_cleanups| {
+                do opt_temp_cleanups.mutate |temp_cleanups| {
                     add_clean_temp_mem(bcx, lldest, binding_info.ty);
                     temp_cleanups.push(lldest);
+                    temp_cleanups
                 }
             }
             TrByRef | TrByImplicitRef => {}
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index efa10dfc2aa..7be6bdb654e 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -391,14 +391,16 @@ pub fn get_tydesc_simple(ccx: @CrateContext, t: ty::t) -> ValueRef {
 
 pub fn get_tydesc(ccx: @CrateContext, t: ty::t) -> @mut tydesc_info {
     match ccx.tydescs.find(&t) {
-      Some(&inf) => inf,
-      _ => {
-        ccx.stats.n_static_tydescs += 1u;
-        let inf = glue::declare_tydesc(ccx, t);
-        ccx.tydescs.insert(t, inf);
-        inf
-      }
+        Some(&inf) => {
+            return inf;
+        }
+        _ => { }
     }
+
+    ccx.stats.n_static_tydescs += 1u;
+    let inf = glue::declare_tydesc(ccx, t);
+    ccx.tydescs.insert(t, inf);
+    return inf;
 }
 
 pub fn set_optimize_for_size(f: ValueRef) {
@@ -888,18 +890,18 @@ pub fn need_invoke(bcx: block) -> bool {
         let current = &mut *cur;
         let kind = &mut *current.kind;
         match *kind {
-          block_scope(ref mut inf) => {
-            for vec::each((*inf).cleanups) |cleanup| {
-                match *cleanup {
-                  clean(_, cleanup_type) | clean_temp(_, _, cleanup_type) => {
-                    if cleanup_type == normal_exit_and_unwind {
-                        return true;
+            block_scope(ref mut inf) => {
+                for vec::each((*inf).cleanups) |cleanup| {
+                    match *cleanup {
+                        clean(_, cleanup_type) | clean_temp(_, _, cleanup_type) => {
+                            if cleanup_type == normal_exit_and_unwind {
+                                return true;
+                            }
+                        }
                     }
-                  }
                 }
             }
-          }
-          _ => ()
+            _ => ()
         }
         cur = match current.parent {
           Some(next) => next,
@@ -1011,12 +1013,12 @@ pub fn add_root_cleanup(bcx: block,
                              ty=%s)",
            bcx.to_str(),
            root_info.scope,
-           root_info.freezes,
+           root_info.freeze,
            val_str(bcx.ccx().tn, root_loc),
            ppaux::ty_to_str(bcx.ccx().tcx, ty));
 
     let bcx_scope = find_bcx_for_scope(bcx, root_info.scope);
-    if root_info.freezes {
+    if root_info.freeze.is_some() {
         add_clean_frozen_root(bcx_scope, root_loc, ty);
     } else {
         add_clean_temp_mem(bcx_scope, root_loc, ty);
@@ -1029,6 +1031,12 @@ pub fn add_root_cleanup(bcx: block,
               Some(NodeInfo { id, _ }) if id == scope_id => {
                 return bcx_sid
               }
+
+              // NOTE This is messier than it ought to be and not really right
+              Some(NodeInfo { callee_id: Some(id), _ }) if id == scope_id => {
+                return bcx_sid
+              }
+
               _ => {
                 match bcx_sid.parent {
                   None => bcx.tcx().sess.bug(
@@ -2484,37 +2492,40 @@ pub fn get_dtor_symbol(ccx: @CrateContext,
                        id: ast::node_id,
                        substs: Option<@param_substs>)
                     -> ~str {
-  let t = ty::node_id_to_type(ccx.tcx, id);
-  match ccx.item_symbols.find(&id) {
-     Some(s) => (/*bad*/copy *s),
-     None if substs.is_none() => {
-       let s = mangle_exported_name(
-           ccx,
-           vec::append(path, ~[path_name((ccx.names)(~"dtor"))]),
-           t);
-       // XXX: Bad copy, use `@str`?
-       ccx.item_symbols.insert(id, copy s);
-       s
-     }
-     None   => {
-       // Monomorphizing, so just make a symbol, don't add
-       // this to item_symbols
-       match substs {
-         Some(ss) => {
-           let mono_ty = ty::subst_tps(ccx.tcx, ss.tys, ss.self_ty, t);
-           mangle_exported_name(
-               ccx,
-               vec::append(path,
-                           ~[path_name((ccx.names)(~"dtor"))]),
-               mono_ty)
-         }
-         None => {
-             ccx.sess.bug(fmt!("get_dtor_symbol: not monomorphizing and \
-               couldn't find a symbol for dtor %?", path));
-         }
-       }
-     }
-  }
+    let t = ty::node_id_to_type(ccx.tcx, id);
+    match ccx.item_symbols.find(&id) {
+        Some(s) => {
+            return /*bad*/copy *s;
+        }
+        None => { }
+    }
+
+    return if substs.is_none() {
+        let s = mangle_exported_name(
+            ccx,
+            vec::append(path, ~[path_name((ccx.names)(~"dtor"))]),
+            t);
+        // XXX: Bad copy, use `@str`?
+        ccx.item_symbols.insert(id, copy s);
+        s
+    } else {
+        // Monomorphizing, so just make a symbol, don't add
+        // this to item_symbols
+        match substs {
+            Some(ss) => {
+                let mono_ty = ty::subst_tps(ccx.tcx, ss.tys, ss.self_ty, t);
+                mangle_exported_name(
+                    ccx,
+                    vec::append(path,
+                                ~[path_name((ccx.names)(~"dtor"))]),
+                    mono_ty)
+            }
+            None => {
+                ccx.sess.bug(fmt!("get_dtor_symbol: not monomorphizing and \
+                                   couldn't find a symbol for dtor %?", path));
+            }
+        }
+    };
 }
 
 pub fn get_item_val(ccx: @CrateContext, id: ast::node_id) -> ValueRef {
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index ad0fea3b4b4..af00257fe16 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -39,7 +39,6 @@ use middle::trans::monomorphize;
 use middle::trans::type_of;
 use middle::ty;
 use middle::typeck;
-use util::common::indenter;
 use util::ppaux::Repr;
 
 use syntax::ast;
@@ -689,7 +688,6 @@ pub fn trans_arg_expr(bcx: block,
            self_mode,
            arg_expr.repr(bcx.tcx()),
            ret_flag.map(|v| bcx.val_str(*v)));
-    let _indenter = indenter();
 
     // translate the arg expr to a datum
     let arg_datumblock = match ret_flag {
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index f8fb0f4b7cf..2ebd696dbfd 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -574,13 +574,17 @@ pub trait get_node_info {
 
 impl get_node_info for @ast::expr {
     fn info(&self) -> Option<NodeInfo> {
-        Some(NodeInfo { id: self.id, span: self.span })
+        Some(NodeInfo {id: self.id,
+                       callee_id: Some(self.callee_id),
+                       span: self.span})
     }
 }
 
 impl get_node_info for ast::blk {
     fn info(&self) -> Option<NodeInfo> {
-        Some(NodeInfo { id: self.node.id, span: self.span })
+        Some(NodeInfo {id: self.node.id,
+                       callee_id: None,
+                       span: self.span})
     }
 }
 
@@ -592,6 +596,7 @@ impl get_node_info for Option<@ast::expr> {
 
 pub struct NodeInfo {
     id: ast::node_id,
+    callee_id: Option<ast::node_id>,
     span: span
 }
 
diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs
index 25f34b8eaa9..3a331e8791b 100644
--- a/src/librustc/middle/trans/consts.rs
+++ b/src/librustc/middle/trans/consts.rs
@@ -195,18 +195,19 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
             match adj.autoref {
                 None => { }
                 Some(ref autoref) => {
-                    assert!(autoref.region == ty::re_static);
-                    assert!(autoref.mutbl != ast::m_mutbl);
                     // Don't copy data to do a deref+ref.
                     let llptr = match maybe_ptr {
                         Some(ptr) => ptr,
                         None => const_addr_of(cx, llconst)
                     };
-                    match autoref.kind {
-                        ty::AutoPtr => {
+                    match *autoref {
+                        ty::AutoUnsafe(m) |
+                        ty::AutoPtr(ty::re_static, m) => {
+                            assert!(m != ast::m_mutbl);
                             llconst = llptr;
                         }
-                        ty::AutoBorrowVec => {
+                        ty::AutoBorrowVec(ty::re_static, m) => {
+                            assert!(m != ast::m_mutbl);
                             let size = machine::llsize_of(cx,
                                                           val_ty(llconst));
                             assert!(abi::slice_elt_base == 0);
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index fa27f652ac8..705d443b115 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -524,8 +524,8 @@ pub impl Datum {
          * case, we will call this function, which will stash a copy
          * away until we exit the scope `scope_id`. */
 
-        debug!("root(scope_id=%?, freezes=%?, self=%?)",
-               root_info.scope, root_info.freezes, self.to_str(bcx.ccx()));
+        debug!("root(root_info=%?, self=%?)",
+               root_info, self.to_str(bcx.ccx()));
 
         if bcx.sess().trace() {
             trans_trace(
@@ -539,7 +539,8 @@ pub impl Datum {
         add_root_cleanup(bcx, root_info, scratch.val, scratch.ty);
 
         // If we need to freeze the box, do that now.
-        if root_info.freezes {
+        if root_info.freeze.is_some() {
+            // NOTE distinguish the two kinds of freezing here
             callee::trans_lang_call(
                 bcx,
                 bcx.tcx().lang_items.borrow_as_imm_fn(),
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index f83562add31..ac6fa7a5a1c 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -146,9 +146,9 @@ use middle::trans::type_of;
 use middle::ty;
 use middle::ty::struct_mutable_fields;
 use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn,
-                 AutoDerefRef, AutoAddEnv};
+                 AutoDerefRef, AutoAddEnv, AutoUnsafe};
 use util::common::indenter;
-use util::ppaux::ty_to_str;
+use util::ppaux::Repr;
 
 use core::cast::transmute;
 use core::hashmap::HashMap;
@@ -201,6 +201,8 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
                 trans_to_datum_unadjusted(bcx, expr)
             });
 
+            debug!("unadjusted datum: %s", datum.to_str(bcx.ccx()));
+
             if adj.autoderefs > 0 {
                 let DatumBlock { bcx: new_bcx, datum: new_datum } =
                     datum.autoderef(bcx, expr.id, adj.autoderefs);
@@ -209,25 +211,24 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
             }
 
             datum = match adj.autoref {
-                None => datum,
-                Some(ref autoref) => {
-                    match autoref.kind {
-                        AutoPtr => {
-                            unpack_datum!(bcx, auto_ref(bcx, datum))
-                        }
-                        AutoBorrowVec => {
-                            unpack_datum!(bcx, auto_slice(bcx, datum))
-                        }
-                        AutoBorrowVecRef => {
-                            unpack_datum!(bcx, auto_slice_and_ref(bcx, datum))
-                        }
-                        AutoBorrowFn => {
-                            // currently, all closure types are
-                            // represented precisely the same, so no
-                            // runtime adjustment is required:
-                            datum
-                        }
-                    }
+                None => {
+                    datum
+                }
+                Some(AutoUnsafe(*)) | // region + unsafe ptrs have same repr
+                Some(AutoPtr(*)) => {
+                    unpack_datum!(bcx, auto_ref(bcx, datum))
+                }
+                Some(AutoBorrowVec(*)) => {
+                    unpack_datum!(bcx, auto_slice(bcx, datum))
+                }
+                Some(AutoBorrowVecRef(*)) => {
+                    unpack_datum!(bcx, auto_slice_and_ref(bcx, datum))
+                }
+                Some(AutoBorrowFn(*)) => {
+                    // currently, all closure types are
+                    // represented precisely the same, so no
+                    // runtime adjustment is required:
+                    datum
                 }
             };
 
@@ -273,7 +274,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
 
         let tcx = bcx.tcx();
         let closure_ty = expr_ty_adjusted(bcx, expr);
-        debug!("add_env(closure_ty=%s)", ty_to_str(tcx, closure_ty));
+        debug!("add_env(closure_ty=%s)", closure_ty.repr(tcx));
         let scratch = scratch_datum(bcx, closure_ty, false);
         let llfn = GEPi(bcx, scratch.val, [0u, abi::fn_field_code]);
         assert!(datum.appropriate_mode() == ByValue);
@@ -612,7 +613,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
             let sigil = ty::ty_closure_sigil(expr_ty);
             debug!("translating fn_block %s with type %s",
                    expr_to_str(expr, tcx.sess.intr()),
-                   ty_to_str(tcx, expr_ty));
+                   expr_ty.repr(tcx));
             return closure::trans_expr_fn(bcx, sigil, decl, body,
                                           expr.id, expr.id,
                                           None, dest);
@@ -1088,6 +1089,9 @@ pub fn trans_local_var(bcx: block, def: ast::def) -> Datum {
                 }
             };
 
+            debug!("def_self() reference, self_info.t=%s",
+                   self_info.t.repr(bcx.tcx()));
+
             // This cast should not be necessary. We should cast self *once*,
             // but right now this conflicts with default methods.
             let real_self_ty = monomorphize_type(bcx, self_info.t);
@@ -1151,7 +1155,7 @@ pub fn with_field_tys<R>(tcx: ty::ctxt,
                     tcx.sess.bug(fmt!(
                         "cannot get field types from the enum type %s \
                          without a node ID",
-                        ty_to_str(tcx, ty)));
+                        ty.repr(tcx)));
                 }
                 Some(node_id) => {
                     match *tcx.def_map.get(&node_id) {
@@ -1173,7 +1177,7 @@ pub fn with_field_tys<R>(tcx: ty::ctxt,
         _ => {
             tcx.sess.bug(fmt!(
                 "cannot get field types from the type %s",
-                ty_to_str(tcx, ty)));
+                ty.repr(tcx)));
         }
     }
 }
diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs
index 7a7f03c2273..40b7a444a3e 100644
--- a/src/librustc/middle/trans/inline.rs
+++ b/src/librustc/middle/trans/inline.rs
@@ -29,26 +29,33 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::def_id,
     -> ast::def_id {
     let _icx = ccx.insn_ctxt("maybe_instantiate_inline");
     match ccx.external.find(&fn_id) {
-      Some(&Some(node_id)) => {
-        // Already inline
-        debug!("maybe_instantiate_inline(%s): already inline as node id %d",
-               ty::item_path_str(ccx.tcx, fn_id), node_id);
-        local_def(node_id)
-      }
-      Some(&None) => fn_id, // Not inlinable
-      None => { // Not seen yet
-        match csearch::maybe_get_item_ast(
+        Some(&Some(node_id)) => {
+            // Already inline
+            debug!("maybe_instantiate_inline(%s): already inline as node id %d",
+                   ty::item_path_str(ccx.tcx, fn_id), node_id);
+            return local_def(node_id);
+        }
+        Some(&None) => {
+            return fn_id; // Not inlinable
+        }
+        None => {
+            // Not seen yet
+        }
+    }
+
+    let csearch_result =
+        csearch::maybe_get_item_ast(
             ccx.tcx, fn_id,
             |a,b,c,d| {
                 astencode::decode_inlined_item(a, b, ccx.maps,
                                                /*bad*/ copy c, d)
-            }) {
-
-          csearch::not_found => {
+            });
+    return match csearch_result {
+        csearch::not_found => {
             ccx.external.insert(fn_id, None);
             fn_id
-          }
-          csearch::found(ast::ii_item(item)) => {
+        }
+        csearch::found(ast::ii_item(item)) => {
             ccx.external.insert(fn_id, Some(item.id));
             ccx.stats.n_inlines += 1;
             if translate { trans_item(ccx, item); }
@@ -122,8 +129,6 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::def_id,
               ccx.external.insert(fn_id, Some((*dtor).node.id));
               local_def((*dtor).node.id)
           }
-        }
-      }
-    }
+    };
 }
 
diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs
index 90f9f93be2b..86b087b937f 100644
--- a/src/librustc/middle/trans/meth.rs
+++ b/src/librustc/middle/trans/meth.rs
@@ -44,6 +44,11 @@ pub fn trans_impl(ccx: @CrateContext, path: path, name: ast::ident,
                   methods: &[@ast::method], generics: &ast::Generics,
                   self_ty: Option<ty::t>, id: ast::node_id) {
     let _icx = ccx.insn_ctxt("impl::trans_impl");
+    let tcx = ccx.tcx;
+
+    debug!("trans_impl(path=%s, name=%s, self_ty=%s, id=%?)",
+           path.repr(tcx), name.repr(tcx), self_ty.repr(tcx), id);
+
     if !generics.ty_params.is_empty() { return; }
     let sub_path = vec::append_one(path, path_name(name));
     for vec::each(methods) |method| {
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index 72ad6dde4f1..98db829370c 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -137,6 +137,9 @@ pub fn monomorphic_fn(ccx: @CrateContext,
       ast_map::node_local(*) => {
           ccx.tcx.sess.bug(~"Can't monomorphize a local")
       }
+      ast_map::node_callee_scope(*) => {
+          ccx.tcx.sess.bug(~"Can't monomorphize a callee-scope")
+      }
       ast_map::node_struct_ctor(_, i, pt) => (pt, i.ident, i.span)
     };
 
@@ -279,6 +282,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
       ast_map::node_trait_method(*) |
       ast_map::node_arg(*) |
       ast_map::node_block(*) |
+      ast_map::node_callee_scope(*) |
       ast_map::node_local(*) => {
         ccx.tcx.sess.bug(fmt!("Can't monomorphize a %?", map_node))
       }
diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs
index 3ccef0dbc4a..a446408d00a 100644
--- a/src/librustc/middle/trans/reachable.rs
+++ b/src/librustc/middle/trans/reachable.rs
@@ -42,19 +42,19 @@ pub fn find_reachable(crate_mod: &_mod, exp_map2: resolve::ExportMap2,
                       tcx: ty::ctxt, method_map: typeck::method_map) -> map {
     let mut rmap = HashSet::new();
     {
-        let cx = ctx {
+        let mut cx = @mut ctx {
             exp_map2: exp_map2,
             tcx: tcx,
             method_map: method_map,
             rmap: &mut rmap
         };
-        traverse_public_mod(&cx, ast::crate_node_id, crate_mod);
-        traverse_all_resources_and_impls(&cx, crate_mod);
+        traverse_public_mod(cx, ast::crate_node_id, crate_mod);
+        traverse_all_resources_and_impls(cx, crate_mod);
     }
     return @rmap;
 }
 
-fn traverse_exports(cx: &ctx, mod_id: node_id) -> bool {
+fn traverse_exports(cx: @mut ctx, mod_id: node_id) -> bool {
     let mut found_export = false;
     match cx.exp_map2.find(&mod_id) {
       Some(ref exp2s) => {
@@ -68,23 +68,25 @@ fn traverse_exports(cx: &ctx, mod_id: node_id) -> bool {
     return found_export;
 }
 
-fn traverse_def_id(cx: &ctx, did: def_id) {
+fn traverse_def_id(cx: @mut ctx, did: def_id) {
     if did.crate != local_crate { return; }
     match cx.tcx.items.find(&did.node) {
         None => (), // This can happen for self, for example
         Some(&ast_map::node_item(item, _)) => traverse_public_item(cx, item),
         Some(&ast_map::node_method(_, impl_id, _)) => traverse_def_id(cx, impl_id),
         Some(&ast_map::node_foreign_item(item, _, _, _)) => {
+            let cx = &mut *cx; // NOTE reborrow @mut
             cx.rmap.insert(item.id);
         }
         Some(&ast_map::node_variant(ref v, _, _)) => {
+            let cx = &mut *cx; // NOTE reborrow @mut
             cx.rmap.insert(v.node.id);
         }
         _ => ()
     }
 }
 
-fn traverse_public_mod(cx: &ctx, mod_id: node_id, m: &_mod) {
+fn traverse_public_mod(cx: @mut ctx, mod_id: node_id, m: &_mod) {
     if !traverse_exports(cx, mod_id) {
         // No exports, so every local item is exported
         for m.items.each |item| {
@@ -93,17 +95,21 @@ fn traverse_public_mod(cx: &ctx, mod_id: node_id, m: &_mod) {
     }
 }
 
-fn traverse_public_item(cx: &ctx, item: @item) {
-    // FIXME #6021: naming rmap shouldn't be necessary
-    let rmap: &mut HashSet<node_id> = cx.rmap;
-    if rmap.contains(&item.id) { return; }
-    rmap.insert(item.id);
+fn traverse_public_item(cx: @mut ctx, item: @item) {
+    {
+        // FIXME #6021: naming rmap shouldn't be necessary
+        let cx = &mut *cx;
+        let rmap: &mut HashSet<node_id> = cx.rmap;
+        if rmap.contains(&item.id) { return; }
+        rmap.insert(item.id);
+    }
+
     match item.node {
       item_mod(ref m) => traverse_public_mod(cx, item.id, m),
       item_foreign_mod(ref nm) => {
           if !traverse_exports(cx, item.id) {
               for nm.items.each |item| {
-                  cx.rmap.insert(item.id);
+                  (&mut *cx).rmap.insert(item.id); // NOTE reborrow @mut
               }
           }
       }
@@ -119,17 +125,17 @@ fn traverse_public_item(cx: &ctx, item: @item) {
                 m.generics.ty_params.len() > 0u ||
                 attr::find_inline_attr(m.attrs) != attr::ia_none
             {
-                cx.rmap.insert(m.id);
+                (&mut *cx).rmap.insert(m.id); // NOTE reborrow @mut
                 traverse_inline_body(cx, &m.body);
             }
         }
       }
       item_struct(ref struct_def, ref generics) => {
         for struct_def.ctor_id.each |&ctor_id| {
-            cx.rmap.insert(ctor_id);
+            (&mut *cx).rmap.insert(ctor_id); // NOTE reborrow @mut
         }
         for struct_def.dtor.each |dtor| {
-            cx.rmap.insert(dtor.node.id);
+            (&mut *cx).rmap.insert(dtor.node.id);
             if generics.ty_params.len() > 0u ||
                 attr::find_inline_attr(dtor.node.attrs) != attr::ia_none
             {
@@ -148,11 +154,13 @@ fn traverse_public_item(cx: &ctx, item: @item) {
     }
 }
 
-fn traverse_ty<'a, 'b>(ty: @Ty, cx: &'b ctx<'a>, v: visit::vt<&'b ctx<'a>>) {
-    // FIXME #6021: naming rmap shouldn't be necessary
-    let rmap: &mut HashSet<node_id> = cx.rmap;
-    if rmap.contains(&ty.id) { return; }
-    rmap.insert(ty.id);
+fn traverse_ty<'a>(ty: @Ty, cx: @mut ctx<'a>, v: visit::vt<@mut ctx<'a>>) {
+    {
+        // FIXME #6021: naming rmap shouldn't be necessary
+        let cx = &mut *cx;
+        if cx.rmap.contains(&ty.id) { return; }
+        cx.rmap.insert(ty.id);
+    }
 
     match ty.node {
       ty_path(p, p_id) => {
@@ -171,9 +179,9 @@ fn traverse_ty<'a, 'b>(ty: @Ty, cx: &'b ctx<'a>, v: visit::vt<&'b ctx<'a>>) {
     }
 }
 
-fn traverse_inline_body(cx: &ctx, body: &blk) {
-    fn traverse_expr<'a, 'b>(e: @expr, cx: &'b ctx<'a>,
-                             v: visit::vt<&'b ctx<'a>>) {
+fn traverse_inline_body(cx: @mut ctx, body: &blk) {
+    fn traverse_expr<'a>(e: @expr, cx: @mut ctx<'a>,
+                         v: visit::vt<@mut ctx<'a>>) {
         match e.node {
           expr_path(_) => {
             match cx.tcx.def_map.find(&e.id) {
@@ -218,7 +226,7 @@ fn traverse_inline_body(cx: &ctx, body: &blk) {
     // Don't ignore nested items: for example if a generic fn contains a
     // generic impl (as in deque::create), we need to monomorphize the
     // impl as well
-    fn traverse_item(i: @item, cx: &ctx, _v: visit::vt<&ctx>) {
+    fn traverse_item(i: @item, cx: @mut ctx, _v: visit::vt<@mut ctx>) {
       traverse_public_item(cx, i);
     }
     visit::visit_block(body, cx, visit::mk_vt(@visit::Visitor {
@@ -228,7 +236,7 @@ fn traverse_inline_body(cx: &ctx, body: &blk) {
     }));
 }
 
-fn traverse_all_resources_and_impls(cx: &ctx, crate_mod: &_mod) {
+fn traverse_all_resources_and_impls(cx: @mut ctx, crate_mod: &_mod) {
     visit::visit_mod(
         crate_mod,
         codemap::dummy_sp(),
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index c7fb1e94adf..28705ac4932 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -183,26 +183,21 @@ pub struct AutoDerefRef {
 
 #[auto_encode]
 #[auto_decode]
-pub struct AutoRef {
-    kind: AutoRefKind,
-    region: Region,
-    mutbl: ast::mutability
-}
-
-#[auto_encode]
-#[auto_decode]
-pub enum AutoRefKind {
+pub enum AutoRef {
     /// Convert from T to &T
-    AutoPtr,
+    AutoPtr(Region, ast::mutability),
 
     /// Convert from @[]/~[]/&[] to &[] (or str)
-    AutoBorrowVec,
+    AutoBorrowVec(Region, ast::mutability),
 
     /// Convert from @[]/~[]/&[] to &&[] (or str)
-    AutoBorrowVecRef,
+    AutoBorrowVecRef(Region, ast::mutability),
 
     /// Convert from @fn()/~fn()/&fn() to &fn()
-    AutoBorrowFn
+    AutoBorrowFn(Region),
+
+    /// Convert from T to *T
+    AutoUnsafe(ast::mutability)
 }
 
 // Stores information about provided methods (a.k.a. default methods) in
@@ -432,11 +427,20 @@ pub enum Region {
     /// A concrete region naming some expression within the current function.
     re_scope(node_id),
 
-    /// Static data that has an "infinite" lifetime.
+    /// Static data that has an "infinite" lifetime. Top in the region lattice.
     re_static,
 
     /// A region variable.  Should not exist after typeck.
-    re_infer(InferRegion)
+    re_infer(InferRegion),
+
+    /// Empty lifetime is for data that is never accessed.
+    /// Bottom in the region lattice. We treat re_empty somewhat
+    /// specially; at least right now, we do not generate instances of
+    /// it during the GLB computations, but rather
+    /// generate an error instead. This is to improve error messages.
+    /// The only way to get an instance of re_empty is to have a region
+    /// variable with no constraints.
+    re_empty,
 }
 
 pub impl Region {
@@ -2874,6 +2878,17 @@ pub fn ty_region(tcx: ctxt,
     }
 }
 
+pub fn replace_fn_sig(cx: ctxt, fsty: &sty, new_sig: FnSig) -> t {
+    match *fsty {
+        ty_bare_fn(ref f) => mk_bare_fn(cx, BareFnTy {sig: new_sig, ..*f}),
+        ty_closure(ref f) => mk_closure(cx, ClosureTy {sig: new_sig, ..*f}),
+        ref s => {
+            cx.sess.bug(
+                fmt!("ty_fn_sig() called on non-fn type: %?", s));
+        }
+    }
+}
+
 pub fn replace_closure_return_type(tcx: ctxt, fn_type: t, ret_type: t) -> t {
     /*!
      *
@@ -2993,26 +3008,26 @@ pub fn adjust_ty(cx: ctxt,
             match adj.autoref {
                 None => adjusted_ty,
                 Some(ref autoref) => {
-                    match autoref.kind {
-                        AutoPtr => {
-                            mk_rptr(cx, autoref.region,
-                                    mt {ty: adjusted_ty,
-                                        mutbl: autoref.mutbl})
+                    match *autoref {
+                        AutoPtr(r, m) => {
+                            mk_rptr(cx, r, mt {ty: adjusted_ty, mutbl: m})
                         }
 
-                        AutoBorrowVec => {
-                            borrow_vec(cx, span, autoref, adjusted_ty)
+                        AutoBorrowVec(r, m) => {
+                            borrow_vec(cx, span, r, m, adjusted_ty)
                         }
 
-                        AutoBorrowVecRef => {
-                            adjusted_ty = borrow_vec(cx, span, autoref,
-                                                     adjusted_ty);
-                            mk_rptr(cx, autoref.region,
-                                    mt {ty: adjusted_ty, mutbl: ast::m_imm})
+                        AutoBorrowVecRef(r, m) => {
+                            adjusted_ty = borrow_vec(cx, span, r, m, adjusted_ty);
+                            mk_rptr(cx, r, mt {ty: adjusted_ty, mutbl: ast::m_imm})
                         }
 
-                        AutoBorrowFn => {
-                            borrow_fn(cx, span, autoref, adjusted_ty)
+                        AutoBorrowFn(r) => {
+                            borrow_fn(cx, span, r, adjusted_ty)
+                        }
+
+                        AutoUnsafe(m) => {
+                            mk_ptr(cx, mt {ty: adjusted_ty, mutbl: m})
                         }
                     }
                 }
@@ -3021,15 +3036,15 @@ pub fn adjust_ty(cx: ctxt,
     };
 
     fn borrow_vec(cx: ctxt, span: span,
-                  autoref: &AutoRef, ty: ty::t) -> ty::t {
+                  r: Region, m: ast::mutability,
+                  ty: ty::t) -> ty::t {
         match get(ty).sty {
             ty_evec(mt, _) => {
-                ty::mk_evec(cx, mt {ty: mt.ty, mutbl: autoref.mutbl},
-                            vstore_slice(autoref.region))
+                ty::mk_evec(cx, mt {ty: mt.ty, mutbl: m}, vstore_slice(r))
             }
 
             ty_estr(_) => {
-                ty::mk_estr(cx, vstore_slice(autoref.region))
+                ty::mk_estr(cx, vstore_slice(r))
             }
 
             ref s => {
@@ -3041,13 +3056,12 @@ pub fn adjust_ty(cx: ctxt,
         }
     }
 
-    fn borrow_fn(cx: ctxt, span: span,
-                 autoref: &AutoRef, ty: ty::t) -> ty::t {
+    fn borrow_fn(cx: ctxt, span: span, r: Region, ty: ty::t) -> ty::t {
         match get(ty).sty {
             ty_closure(ref fty) => {
                 ty::mk_closure(cx, ClosureTy {
                     sigil: BorrowedSigil,
-                    region: autoref.region,
+                    region: r,
                     ..copy *fty
                 })
             }
@@ -3062,6 +3076,18 @@ pub fn adjust_ty(cx: ctxt,
     }
 }
 
+pub impl AutoRef {
+    fn map_region(&self, f: &fn(Region) -> Region) -> AutoRef {
+        match *self {
+            ty::AutoPtr(r, m) => ty::AutoPtr(f(r), m),
+            ty::AutoBorrowVec(r, m) => ty::AutoBorrowVec(f(r), m),
+            ty::AutoBorrowVecRef(r, m) => ty::AutoBorrowVecRef(f(r), m),
+            ty::AutoBorrowFn(r) => ty::AutoBorrowFn(f(r)),
+            ty::AutoUnsafe(m) => ty::AutoUnsafe(m),
+        }
+    }
+}
+
 pub struct ParamsTy {
     params: ~[t],
     ty: t
@@ -3986,7 +4012,7 @@ pub fn lookup_field_type(tcx: ctxt,
     }
     else {
         match tcx.tcache.find(&id) {
-           Some(tpt) => tpt.ty,
+           Some(&ty_param_bounds_and_ty {ty, _}) => ty,
            None => {
                let tpt = csearch::get_field_type(tcx, struct_id, id);
                tcx.tcache.insert(id, tpt);
diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs
index 7f0066a1aa2..0c9b61164d2 100644
--- a/src/librustc/middle/typeck/check/_match.rs
+++ b/src/librustc/middle/typeck/check/_match.rs
@@ -118,8 +118,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path,
 
             // Assign the pattern the type of the *enum*, not the variant.
             let enum_tpt = ty::lookup_item_type(tcx, enm);
-            instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id,
-                             pcx.block_region);
+            instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id);
 
             // check that the type of the value being matched is a subtype
             // of the type of the pattern:
@@ -159,8 +158,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path,
             } else {
                 ctor_tpt
             };
-            instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id,
-                             pcx.block_region);
+            instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id);
 
             // Check that the type of the value being matched is a subtype of
             // the type of the pattern.
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index fb5b53d9400..0cc2ddd32b4 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -119,7 +119,8 @@ pub fn lookup(
         // In a call `a.b::<X, Y, ...>(...)`:
         expr: @ast::expr,                   // The expression `a.b(...)`.
         self_expr: @ast::expr,              // The expression `a`.
-        callee_id: node_id,                 // Where to store `a.b`'s type
+        callee_id: node_id,                 /* Where to store `a.b`'s type,
+                                             * also the scope of the call */ 
         m_name: ast::ident,                 // The ident `b`.
         self_ty: ty::t,                     // The type of `a`.
         supplied_tps: &[ty::t],             // The list of types X, Y, ... .
@@ -127,7 +128,7 @@ pub fn lookup(
         check_traits: CheckTraitsFlag,      // Whether we check traits only.
         autoderef_receiver: AutoderefReceiverFlag)
      -> Option<method_map_entry> {
-    let mut impl_dups = HashSet::new();
+    let mut impl_dups = @mut HashSet::new();
     let lcx = LookupContext {
         fcx: fcx,
         expr: expr,
@@ -135,7 +136,7 @@ pub fn lookup(
         callee_id: callee_id,
         m_name: m_name,
         supplied_tps: supplied_tps,
-        impl_dups: &mut impl_dups,
+        impl_dups: impl_dups,
         inherent_candidates: @mut ~[],
         extension_candidates: @mut ~[],
         deref_args: deref_args,
@@ -154,7 +155,7 @@ pub struct LookupContext<'self> {
     callee_id: node_id,
     m_name: ast::ident,
     supplied_tps: &'self [ty::t],
-    impl_dups: &'self mut HashSet<def_id>,
+    impl_dups: @mut HashSet<def_id>,
     inherent_candidates: @mut ~[Candidate],
     extension_candidates: @mut ~[Candidate],
     deref_args: check::DerefArgs,
@@ -640,7 +641,7 @@ pub impl<'self> LookupContext<'self> {
         /*!
          *
          * In the event that we are invoking a method with a receiver
-         * of a linear borrowed type like `&mut T` or `&mut [T]`,
+         * of a borrowed type like `&T`, `&mut T`, or `&mut [T]`,
          * we will "reborrow" the receiver implicitly.  For example, if
          * you have a call `r.inc()` and where `r` has type `&mut T`,
          * then we treat that like `(&mut *r).inc()`.  This avoids
@@ -657,26 +658,19 @@ pub impl<'self> LookupContext<'self> {
 
         let tcx = self.tcx();
         return match ty::get(self_ty).sty {
-            ty::ty_rptr(_, self_mt) if self_mt.mutbl == m_mutbl => {
-                let region = self.infcx().next_region_var(self.expr.span,
-                                                          self.expr.id);
+            ty::ty_rptr(_, self_mt) => {
+                let region = self.infcx().next_region_var_nb(self.expr.span);
                 (ty::mk_rptr(tcx, region, self_mt),
                  ty::AutoDerefRef(ty::AutoDerefRef {
                      autoderefs: autoderefs+1,
-                     autoref: Some(ty::AutoRef {kind: AutoPtr,
-                                                region: region,
-                                                mutbl: self_mt.mutbl})}))
+                     autoref: Some(ty::AutoPtr(region, self_mt.mutbl))}))
             }
-            ty::ty_evec(self_mt, vstore_slice(_))
-            if self_mt.mutbl == m_mutbl => {
-                let region = self.infcx().next_region_var(self.expr.span,
-                                                          self.expr.id);
+            ty::ty_evec(self_mt, vstore_slice(_)) => {
+                let region = self.infcx().next_region_var_nb(self.expr.span);
                 (ty::mk_evec(tcx, self_mt, vstore_slice(region)),
                  ty::AutoDerefRef(ty::AutoDerefRef {
-                    autoderefs: autoderefs,
-                    autoref: Some(ty::AutoRef {kind: AutoBorrowVec,
-                                               region: region,
-                                               mutbl: self_mt.mutbl})}))
+                     autoderefs: autoderefs,
+                     autoref: Some(ty::AutoBorrowVec(region, self_mt.mutbl))}))
             }
             _ => {
                 (self_ty,
@@ -793,7 +787,7 @@ pub impl<'self> LookupContext<'self> {
 
     fn search_for_some_kind_of_autorefd_method(
         &self,
-        kind: AutoRefKind,
+        kind: &fn(Region, ast::mutability) -> ty::AutoRef,
         autoderefs: uint,
         mutbls: &[ast::mutability],
         mk_autoref_ty: &fn(ast::mutability, ty::Region) -> ty::t)
@@ -801,8 +795,7 @@ pub impl<'self> LookupContext<'self> {
     {
         // This is hokey. We should have mutability inference as a
         // variable.  But for now, try &const, then &, then &mut:
-        let region = self.infcx().next_region_var(self.expr.span,
-                                                  self.expr.id);
+        let region = self.infcx().next_region_var_nb(self.expr.span);
         for mutbls.each |mutbl| {
             let autoref_ty = mk_autoref_ty(*mutbl, region);
             match self.search_for_method(autoref_ty) {
@@ -812,12 +805,7 @@ pub impl<'self> LookupContext<'self> {
                         self.self_expr.id,
                         @ty::AutoDerefRef(ty::AutoDerefRef {
                             autoderefs: autoderefs,
-                            autoref: Some(ty::AutoRef {
-                                kind: kind,
-                                region: region,
-                                mutbl: *mutbl,
-                            }),
-                        }));
+                            autoref: Some(kind(region, *mutbl))}));
                     return Some(mme);
                 }
             }
@@ -1024,8 +1012,7 @@ pub impl<'self> LookupContext<'self> {
         let (_, opt_transformed_self_ty, fn_sig) =
             replace_bound_regions_in_fn_sig(
                 tcx, @Nil, Some(transformed_self_ty), &bare_fn_ty.sig,
-                |_br| self.fcx.infcx().next_region_var(
-                    self.expr.span, self.expr.id));
+                |_br| self.fcx.infcx().next_region_var_nb(self.expr.span));
         let transformed_self_ty = opt_transformed_self_ty.get();
         let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {sig: fn_sig, ..bare_fn_ty});
         debug!("after replacing bound regions, fty=%s", self.ty_to_str(fty));
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index b9f3de873cf..84fc40f6954 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -923,11 +923,9 @@ pub impl FnCtxt {
 
     fn region_var_if_parameterized(&self,
                                    rp: Option<ty::region_variance>,
-                                   span: span,
-                                   lower_bound: ty::Region)
+                                   span: span)
                                 -> Option<ty::Region> {
-        rp.map(
-            |_rp| self.infcx().next_region_var_with_lb(span, lower_bound))
+        rp.map(|_rp| self.infcx().next_region_var_nb(span))
     }
 
     fn type_error_message(&self,
@@ -1108,8 +1106,7 @@ pub fn impl_self_ty(vcx: &VtableContext,
     };
 
     let self_r = if region_param.is_some() {
-        Some(vcx.infcx.next_region_var(location_info.span,
-                                         location_info.id))
+        Some(vcx.infcx.next_region_var_nb(location_info.span))
     } else {
         None
     };
@@ -1317,9 +1314,18 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         // that they appear in call position.
         check_expr(fcx, f);
 
+        // Store the type of `f` as the type of the callee
+        let fn_ty = fcx.expr_ty(f);
+
+        // NOTE here we write the callee type before regions have been
+        // substituted; in the method case, we write the type after
+        // regions have been substituted. Methods are correct, but it
+        // is awkward to deal with this now. Best thing would I think
+        // be to just have a separate "callee table" that contains the
+        // FnSig and not a general purpose ty::t
+        fcx.write_ty(call_expr.callee_id, fn_ty);
 
         // Extract the function signature from `in_fty`.
-        let fn_ty = fcx.expr_ty(f);
         let fn_sty = structure_of(fcx, f.span, fn_ty);
 
         // FIXME(#3678) For now, do not permit calls to C abi functions.
@@ -1356,7 +1362,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         let (_, _, fn_sig) =
             replace_bound_regions_in_fn_sig(
                 fcx.tcx(), @Nil, None, &fn_sig,
-                |_br| fcx.infcx().next_region_var(call_expr.span, call_expr.id));
+                |_br| fcx.infcx().next_region_var_nb(call_expr.span));
 
         // Call the generic checker.
         check_argument_types(fcx, call_expr.span, fn_sig.inputs, f,
@@ -1936,9 +1942,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
 
         // Generate the struct type.
         let self_region =
-            fcx.region_var_if_parameterized(region_parameterized,
-                                            span,
-                                            ty::re_scope(id));
+            fcx.region_var_if_parameterized(region_parameterized, span);
         let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count);
         let substitutions = substs {
             self_r: self_region,
@@ -2024,9 +2028,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
 
         // Generate the enum type.
         let self_region =
-            fcx.region_var_if_parameterized(region_parameterized,
-                                            span,
-                                            ty::re_scope(id));
+            fcx.region_var_if_parameterized(region_parameterized, span);
         let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count);
         let substitutions = substs {
             self_r: self_region,
@@ -2366,13 +2368,12 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         // (and how long it is valid), which we don't know yet until type
         // inference is complete.
         //
-        // Therefore, here we simply generate a region variable with
-        // the current expression as a lower bound.  The region
-        // inferencer will then select the ultimate value.  Finally,
-        // borrowck is charged with guaranteeing that the value whose
-        // address was taken can actually be made to live as long as
-        // it needs to live.
-        let region = fcx.infcx().next_region_var(expr.span, expr.id);
+        // Therefore, here we simply generate a region variable.  The
+        // region inferencer will then select the ultimate value.
+        // Finally, borrowck is charged with guaranteeing that the
+        // value whose address was taken can actually be made to live
+        // as long as it needs to live.
+        let region = fcx.infcx().next_region_var_nb(expr.span);
 
         let tm = ty::mt { ty: fcx.expr_ty(oprnd), mutbl: mutbl };
         let oprnd_t = if ty::type_is_error(tm.ty) {
@@ -2389,8 +2390,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         let defn = lookup_def(fcx, pth.span, id);
 
         let tpt = ty_param_bounds_and_ty_for_def(fcx, expr.span, defn);
-        let region_lb = ty::re_scope(expr.id);
-        instantiate_path(fcx, pth, tpt, expr.span, expr.id, region_lb);
+        instantiate_path(fcx, pth, tpt, expr.span, expr.id);
       }
       ast::expr_inline_asm(ref ia) => {
           fcx.require_unsafe(expr.span, ~"use of inline assembly");
@@ -3258,8 +3258,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
                         pth: @ast::Path,
                         tpt: ty_param_bounds_and_ty,
                         span: span,
-                        node_id: ast::node_id,
-                        region_lb: ty::Region) {
+                        node_id: ast::node_id) {
     debug!(">>> instantiate_path");
 
     let ty_param_count = tpt.generics.type_param_defs.len();
@@ -3285,8 +3284,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
         }
       }
       None => { // no lifetime parameter supplied, insert default
-        fcx.region_var_if_parameterized(
-            tpt.generics.region_param, span, region_lb)
+        fcx.region_var_if_parameterized(tpt.generics.region_param, span)
       }
     };
 
@@ -3370,7 +3368,7 @@ pub fn ast_expr_vstore_to_vstore(fcx: @mut FnCtxt,
         ast::expr_vstore_uniq => ty::vstore_uniq,
         ast::expr_vstore_box | ast::expr_vstore_mut_box => ty::vstore_box,
         ast::expr_vstore_slice | ast::expr_vstore_mut_slice => {
-            let r = fcx.infcx().next_region_var(e.span, e.id);
+            let r = fcx.infcx().next_region_var_nb(e.span);
             ty::vstore_slice(r)
         }
     }
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index cb2b854276d..1c35c911b14 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -28,16 +28,15 @@ this point a bit better.
 */
 
 use middle::freevars::get_freevars;
-use middle::pat_util::pat_bindings;
 use middle::ty::{re_scope};
 use middle::ty;
 use middle::typeck::check::FnCtxt;
-use middle::typeck::check::lookup_def;
 use middle::typeck::check::regionmanip::relate_nested_regions;
 use middle::typeck::infer::resolve_and_force_all_but_regions;
 use middle::typeck::infer::resolve_type;
 use util::ppaux::{note_and_explain_region, ty_to_str,
                   region_to_str};
+use middle::pat_util;
 
 use syntax::ast::{ManagedSigil, OwnedSigil, BorrowedSigil};
 use syntax::ast::{def_arg, def_binding, def_local, def_self, def_upvar};
@@ -73,7 +72,11 @@ fn encl_region_of_def(fcx: @mut FnCtxt, def: ast::def) -> ty::Region {
 }
 
 pub impl Rcx {
-    fn resolve_type(@mut self, unresolved_ty: ty::t) -> ty::t {
+    fn tcx(&self) -> ty::ctxt {
+        self.fcx.ccx.tcx
+    }
+
+    fn resolve_type(&mut self, unresolved_ty: ty::t) -> ty::t {
         /*!
          * Try to resolve the type for the given node, returning
          * t_err if an error results.  Note that we never care
@@ -149,10 +152,17 @@ pub fn regionck_fn(fcx: @mut FnCtxt, blk: &ast::blk) {
 
 fn regionck_visitor() -> rvt {
     visit::mk_vt(@visit::Visitor {visit_item: visit_item,
-                                  visit_stmt: visit_stmt,
                                   visit_expr: visit_expr,
-                                  visit_block: visit_block,
+
+                                  // NOTE this should be visit_pat
+                                  // but causes errors in formal
+                                  // arguments in closures due to
+                                  // #XYZ!
+                                  //visit_pat: visit_pat,
+                                  visit_arm: visit_arm,
                                   visit_local: visit_local,
+
+                                  visit_block: visit_block,
                                   .. *visit::default_visitor()})
 }
 
@@ -160,44 +170,103 @@ fn visit_item(_item: @ast::item, _rcx: @mut Rcx, _v: rvt) {
     // Ignore items
 }
 
-fn visit_local(l: @ast::local, rcx: @mut Rcx, v: rvt) {
-    // Check to make sure that the regions in all local variables are
-    // within scope.
-    //
-    // Note: we do this here rather than in visit_pat because we do
-    // not wish to constrain the regions in *patterns* in quite the
-    // same way.  `visit_node()` guarantees that the region encloses
-    // the node in question, which ultimately constrains the regions
-    // in patterns to enclose the match expression as a whole.  But we
-    // want them to enclose the *arm*.  However, regions in patterns
-    // must either derive from the discriminant or a ref pattern: in
-    // the case of the discriminant, the regions will be constrained
-    // when the type of the discriminant is checked.  In the case of a
-    // ref pattern, the variable is created with a suitable lower
-    // bound.
-    let e = rcx.errors_reported;
-    (v.visit_pat)(l.node.pat, rcx, v);
-    let def_map = rcx.fcx.ccx.tcx.def_map;
-    do pat_bindings(def_map, l.node.pat) |_bm, id, sp, _path| {
-        visit_node(id, sp, rcx);
-    }
-    if e != rcx.errors_reported {
-        return; // if decl has errors, skip initializer expr
-    }
+fn visit_block(b: &ast::blk, rcx: @mut Rcx, v: rvt) {
+    rcx.fcx.tcx().region_maps.record_cleanup_scope(b.node.id);
+    visit::visit_block(b, rcx, v);
+}
 
-    (v.visit_ty)(l.node.ty, rcx, v);
-    for l.node.init.each |i| {
-        (v.visit_expr)(*i, rcx, v);
+fn visit_arm(arm: &ast::arm, rcx: @mut Rcx, v: rvt) {
+    // see above
+    for arm.pats.each |&p| {
+        constrain_bindings_in_pat(p, rcx);
     }
+
+    visit::visit_arm(arm, rcx, v);
 }
 
-fn visit_block(b: &ast::blk, rcx: @mut Rcx, v: rvt) {
-    visit::visit_block(b, rcx, v);
+fn visit_local(l: @ast::local, rcx: @mut Rcx, v: rvt) {
+    // see above
+    constrain_bindings_in_pat(l.node.pat, rcx);
+    visit::visit_local(l, rcx, v);
+}
+
+fn constrain_bindings_in_pat(pat: @ast::pat, rcx: @mut Rcx) {
+    let tcx = rcx.fcx.tcx();
+    debug!("regionck::visit_pat(pat=%s)", pat.repr(tcx));
+    do pat_util::pat_bindings(tcx.def_map, pat) |_, id, span, _| {
+        // If we have a variable that contains region'd data, that
+        // data will be accessible from anywhere that the variable is
+        // accessed. We must be wary of loops like this:
+        //
+        //     // from src/test/compile-fail/borrowck-lend-flow.rs
+        //     let mut v = ~3, w = ~4;
+        //     let mut x = &mut w;
+        //     loop {
+        //         **x += 1;   // (2)
+        //         borrow(v);  //~ ERROR cannot borrow
+        //         x = &mut v; // (1)
+        //     }
+        //
+        // Typically, we try to determine the region of a borrow from
+        // those points where it is dereferenced. In this case, one
+        // might imagine that the lifetime of `x` need only be the
+        // body of the loop. But of course this is incorrect because
+        // the pointer that is created at point (1) is consumed at
+        // point (2), meaning that it must be live across the loop
+        // iteration. The easiest way to guarantee this is to require
+        // that the lifetime of any regions that appear in a
+        // variable's type enclose at least the variable's scope.
+
+        let encl_region = tcx.region_maps.encl_region(id);
+        constrain_regions_in_type_of_node(rcx, id, encl_region, span);
+    }
 }
 
 fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
     debug!("regionck::visit_expr(e=%s)", rcx.fcx.expr_to_str(expr));
 
+    let has_method_map = rcx.fcx.inh.method_map.contains_key(&expr.id);
+
+    // Record cleanup scopes, which are used by borrowck to decide the
+    // maximum lifetime of a temporary rvalue.  These were derived by
+    // examining where trans creates block scopes, not because this
+    // reflects some principled decision around temporary lifetimes.
+    // Ordinarily this would seem like something that should be setup
+    // in region, but we need to know which uses of operators are
+    // overloaded.  See #3511.
+    let tcx = rcx.fcx.tcx();
+    match expr.node {
+        ast::expr_index(*) |
+        ast::expr_binary(*) |
+        ast::expr_assign_op(*) |
+        ast::expr_unary(*) if has_method_map => {
+            tcx.region_maps.record_cleanup_scope(expr.id);
+        }
+        ast::expr_binary(ast::and, lhs, rhs) |
+        ast::expr_binary(ast::or, lhs, rhs) => {
+            tcx.region_maps.record_cleanup_scope(lhs.id);
+            tcx.region_maps.record_cleanup_scope(rhs.id);
+        }
+        ast::expr_call(*) |
+        ast::expr_method_call(*) => {
+            tcx.region_maps.record_cleanup_scope(expr.id);
+        }
+        ast::expr_match(_, ref arms) => {
+            tcx.region_maps.record_cleanup_scope(expr.id);
+            for arms.each |arm| {
+                for arm.guard.each |guard| {
+                    tcx.region_maps.record_cleanup_scope(guard.id);
+                }
+            }
+        }
+        ast::expr_while(cond, ref body) => {
+            tcx.region_maps.record_cleanup_scope(cond.id);
+            tcx.region_maps.record_cleanup_scope(body.node.id);
+        }
+        _ => {}
+    }
+
+    // Check any autoderefs or autorefs that appear.
     for rcx.fcx.inh.adjustments.find(&expr.id).each |&adjustment| {
         debug!("adjustment=%?", adjustment);
         match *adjustment {
@@ -208,6 +277,13 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
                 constrain_derefs(rcx, expr, autoderefs, expr_ty);
                 for opt_autoref.each |autoref| {
                     guarantor::for_autoref(rcx, expr, autoderefs, autoref);
+
+                    // Require that the resulting region encompasses
+                    // the current node.
+                    //
+                    // FIXME(#5074) remove to support nested method calls
+                    constrain_regions_in_type_of_node(
+                        rcx, expr.id, ty::re_scope(expr.id), expr.span);
                 }
             }
             _ => {}
@@ -215,58 +291,40 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
     }
 
     match expr.node {
-        ast::expr_path(*) => {
-            // Avoid checking the use of local variables, as we
-            // already check their definitions.  The def'n always
-            // encloses the use.  So if the def'n is enclosed by the
-            // region, then the uses will also be enclosed (and
-            // otherwise, an error will have been reported at the
-            // def'n site).
-            match lookup_def(rcx.fcx, expr.span, expr.id) {
-                ast::def_local(*) | ast::def_arg(*) |
-                ast::def_upvar(*) => return,
-                _ => ()
-            }
+        ast::expr_call(callee, ref args, _) => {
+            constrain_callee(rcx, expr, callee);
+            constrain_call(rcx, expr, None, *args, false);
         }
 
-        ast::expr_call(callee, ref args, _) => {
-            // Check for a.b() where b is a method.  Ensure that
-            // any types in the callee are valid for the entire
-            // method call.
-
-            // FIXME(#3387)--we should really invoke
-            // `constrain_auto_ref()` on all exprs.  But that causes a
-            // lot of spurious errors because of how the region
-            // hierarchy is setup.
-            if rcx.fcx.inh.method_map.contains_key(&callee.id) {
-                match callee.node {
-                    ast::expr_field(base, _, _) => {
-                        constrain_auto_ref(rcx, base);
-                    }
-                    _ => {
-                        // This can happen if you have code like
-                        // (x[0])() where `x[0]` is overloaded.  Just
-                        // ignore it.
-                    }
-                }
-            } else {
-                constrain_auto_ref(rcx, callee);
-            }
+        ast::expr_method_call(arg0, _, _, ref args, _) => {
+            constrain_call(rcx, expr, Some(arg0), *args, false);
+        }
 
-            for args.each |arg| {
-                constrain_auto_ref(rcx, *arg);
-            }
+        ast::expr_index(lhs, rhs) |
+        ast::expr_assign_op(_, lhs, rhs) |
+        ast::expr_binary(_, lhs, rhs) if has_method_map => {
+            // As `expr_method_call`, but the call is via an
+            // overloaded op.  Note that we (sadly) currently use an
+            // implicit "by ref" sort of passing style here.  This
+            // should be converted to an adjustment!
+            constrain_call(rcx, expr, Some(lhs), [rhs], true);
         }
 
-        ast::expr_method_call(rcvr, _, _, ref args, _) => {
-            // Check for a.b() where b is a method.  Ensure that
-            // any types in the callee are valid for the entire
-            // method call.
+        ast::expr_unary(_, lhs) if has_method_map => {
+            // As above.
+            constrain_call(rcx, expr, Some(lhs), [], true);
+        }
 
-            constrain_auto_ref(rcx, rcvr);
-            for args.each |arg| {
-                constrain_auto_ref(rcx, *arg);
-            }
+        ast::expr_unary(ast::deref, base) => {
+            // For *a, the lifetime of a must enclose the deref
+            let base_ty = rcx.resolve_node_type(base.id);
+            constrain_derefs(rcx, expr, 1, base_ty);
+        }
+
+        ast::expr_index(vec_expr, _) => {
+            // For a[b], the lifetime of a must enclose the deref
+            let vec_type = rcx.resolve_expr_type_adjusted(vec_expr);
+            constrain_index(rcx, expr, vec_type);
         }
 
         ast::expr_cast(source, _) => {
@@ -294,18 +352,18 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
             }
         }
 
-        ast::expr_index(vec_expr, _) => {
-            let vec_type = rcx.resolve_expr_type_adjusted(vec_expr);
-            constrain_index(rcx, expr, vec_type);
-        }
-
-        ast::expr_unary(ast::deref, base) => {
-            let base_ty = rcx.resolve_node_type(base.id);
-            constrain_derefs(rcx, expr, 1, base_ty);
-        }
-
         ast::expr_addr_of(_, base) => {
             guarantor::for_addr_of(rcx, expr, base);
+
+            // Require that when you write a `&expr` expression, the
+            // resulting pointer has a lifetime that encompasses the
+            // `&expr` expression itself. Note that we constraining
+            // the type of the node expr.id here *before applying
+            // adjustments*.
+            //
+            // FIXME(#5074) nested method calls requires that this rule change
+            let ty0 = rcx.resolve_node_type(expr.id);
+            constrain_regions_in_type(rcx, ty::re_scope(expr.id), expr.span, ty0);
         }
 
         ast::expr_match(discr, ref arms) => {
@@ -313,6 +371,8 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
         }
 
         ast::expr_fn_block(*) => {
+            // The lifetime of a block fn must not outlive the variables
+            // it closes over
             let function_type = rcx.resolve_node_type(expr.id);
             match ty::get(function_type).sty {
                 ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil,
@@ -326,46 +386,107 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
         _ => ()
     }
 
-    if !visit_node(expr.id, expr.span, rcx) { return; }
     visit::visit_expr(expr, rcx, v);
 }
 
-fn visit_stmt(s: @ast::stmt, rcx: @mut Rcx, v: rvt) {
-    visit::visit_stmt(s, rcx, v);
-}
+fn constrain_callee(rcx: @mut Rcx,
+                    call_expr: @ast::expr,
+                    callee_expr: @ast::expr)
+{
+    let tcx = rcx.fcx.tcx();
 
-fn visit_node(id: ast::node_id, span: span, rcx: @mut Rcx) -> bool {
-    /*!
-     *
-     * checks the type of the node `id` and reports an error if it
-     * references a region that is not in scope for that node.
-     * Returns false if an error is reported; this is used to cause us
-     * to cut off region checking for that subtree to avoid reporting
-     * tons of errors. */
-
-    let fcx = rcx.fcx;
-
-    // find the region where this expr evaluation is taking place
-    let tcx = fcx.ccx.tcx;
-    let encl_region = match tcx.region_maps.opt_encl_scope(id) {
-        None => ty::re_static,
-        Some(r) => ty::re_scope(r)
-    };
-
-    // Otherwise, look at the type and see if it is a region pointer.
-    constrain_regions_in_type_of_node(rcx, id, encl_region, span)
+    let call_region = ty::re_scope(call_expr.id);
+
+    let callee_ty = rcx.resolve_node_type(call_expr.callee_id);
+    if ty::type_is_error(callee_ty) {
+        return;
+    }
+
+    match ty::get(callee_ty).sty {
+        ty::ty_bare_fn(*) => { }
+        ty::ty_closure(ref closure_ty) => {
+            match rcx.fcx.mk_subr(true, callee_expr.span,
+                                  call_region, closure_ty.region) {
+                result::Err(_) => {
+                    tcx.sess.span_err(
+                        callee_expr.span,
+                        fmt!("cannot invoke closure outside of its lifetime"));
+                    note_and_explain_region(
+                        tcx,
+                        "the closure is only valid for ",
+                        closure_ty.region,
+                        "");
+                }
+                result::Ok(_) => {}
+            }
+        }
+        _ => {
+            tcx.sess.span_bug(
+                callee_expr.span,
+                fmt!("Calling non-function: %s", callee_ty.repr(tcx)));
+        }
+    }
 }
 
-fn encl_region_or_static(rcx: @mut Rcx, expr: @ast::expr) -> ty::Region {
-    // FIXME(#3850) --- interactions with modes compel overly large granularity
-    // that is, we would probably prefer to just return re_scope(expr.id)
-    // here but we cannot just yet.
+fn constrain_call(rcx: @mut Rcx,
+                  // might be expr_call, expr_method_call, or an overloaded
+                  // operator
+                  call_expr: @ast::expr,
+                  receiver: Option<@ast::expr>,
+                  arg_exprs: &[@ast::expr],
+                  implicitly_ref_args: bool)
+{
+    //! Invoked on every call site (i.e., normal calls, method calls,
+    //! and overloaded operators). Constrains the regions which appear
+    //! in the type of the function. Also constrains the regions that
+    //! appear in the arguments appropriately.
 
     let tcx = rcx.fcx.tcx();
-    match tcx.region_maps.opt_encl_scope(expr.id) {
-        Some(s) => ty::re_scope(s),
-        None => ty::re_static // occurs in constants
+    debug!("constrain_call(call_expr=%s, implicitly_ref_args=%?)",
+           call_expr.repr(tcx), implicitly_ref_args);
+    let callee_ty = rcx.resolve_node_type(call_expr.callee_id);
+    if ty::type_is_error(callee_ty) {
+        return;
     }
+    let fn_sig = ty::ty_fn_sig(callee_ty);
+
+    // `callee_region` is the scope representing the time in which the
+    // call occurs.
+    //
+    // FIXME(#5074) to support nested method calls, should be callee_id
+    let callee_scope = call_expr.id;
+    let callee_region = ty::re_scope(callee_scope);
+
+    for fn_sig.inputs.eachi |i, input| {
+        // ensure that any regions appearing in the argument type are
+        // valid for at least the lifetime of the function:
+        constrain_regions_in_type_of_node(
+            rcx, arg_exprs[i].id, callee_region, arg_exprs[i].span);
+
+        // unfortunately, there are two means of taking implicit
+        // references, and we need to propagate constraints as a
+        // result. modes are going away and the "DerefArgs" code
+        // should be ported to use adjustments
+        ty::set_default_mode(tcx, input.mode, ast::by_copy);
+        let is_by_ref = ty::resolved_mode(tcx, input.mode) == ast::by_ref;
+        if implicitly_ref_args || is_by_ref {
+            guarantor::for_by_ref(rcx, arg_exprs[i], callee_scope);
+        }
+    }
+
+    // as loop above, but for receiver
+    for receiver.each |&r| {
+        constrain_regions_in_type_of_node(
+            rcx, r.id, callee_region, r.span);
+        if implicitly_ref_args {
+            guarantor::for_by_ref(rcx, r, callee_scope);
+        }
+    }
+
+    // constrain regions that may appear in the return type to be
+    // valid for the function call:
+    constrain_regions_in_type(
+        rcx, callee_region, call_expr.span, fn_sig.output);
 }
 
 fn constrain_derefs(rcx: @mut Rcx,
@@ -379,9 +500,8 @@ fn constrain_derefs(rcx: @mut Rcx,
      * pointer being derefenced, the lifetime of the pointer includes
      * the deref expr.
      */
-
     let tcx = rcx.fcx.tcx();
-    let r_deref_expr = encl_region_or_static(rcx, deref_expr);
+    let r_deref_expr = ty::re_scope(deref_expr.id);
     for uint::range(0, derefs) |i| {
         debug!("constrain_derefs(deref_expr=%s, derefd_ty=%s, derefs=%?/%?",
                rcx.fcx.expr_to_str(deref_expr),
@@ -390,19 +510,8 @@ fn constrain_derefs(rcx: @mut Rcx,
 
         match ty::get(derefd_ty).sty {
             ty::ty_rptr(r_ptr, _) => {
-                match rcx.fcx.mk_subr(true, deref_expr.span, r_deref_expr, r_ptr) {
-                    result::Ok(*) => {}
-                    result::Err(*) => {
-                        tcx.sess.span_err(
-                            deref_expr.span,
-                            fmt!("dereference of reference outside its lifetime"));
-                        note_and_explain_region(
-                            tcx,
-                            "the reference is only valid for ",
-                            r_ptr,
-                            "");
-                    }
-                }
+                mk_subregion_due_to_derefence(rcx, deref_expr.span,
+                                              r_deref_expr, r_ptr);
             }
 
             _ => {}
@@ -417,6 +526,27 @@ fn constrain_derefs(rcx: @mut Rcx,
     }
 }
 
+pub fn mk_subregion_due_to_derefence(rcx: @mut Rcx,
+                                     deref_span: span,
+                                     minimum_lifetime: ty::Region,
+                                     maximum_lifetime: ty::Region) {
+    match rcx.fcx.mk_subr(true, deref_span,
+                          minimum_lifetime, maximum_lifetime) {
+        result::Ok(*) => {}
+        result::Err(*) => {
+            rcx.tcx().sess.span_err(
+                deref_span,
+                fmt!("dereference of reference outside its lifetime"));
+            note_and_explain_region(
+                rcx.tcx(),
+                "the reference is only valid for ",
+                maximum_lifetime,
+                "");
+        }
+    }
+}
+
+
 fn constrain_index(rcx: @mut Rcx,
                    index_expr: @ast::expr,
                    indexed_ty: ty::t)
@@ -433,7 +563,7 @@ fn constrain_index(rcx: @mut Rcx,
            rcx.fcx.expr_to_str(index_expr),
            rcx.fcx.infcx().ty_to_str(indexed_ty));
 
-    let r_index_expr = encl_region_or_static(rcx, index_expr);
+    let r_index_expr = ty::re_scope(index_expr.id);
     match ty::get(indexed_ty).sty {
         ty::ty_estr(ty::vstore_slice(r_ptr)) |
         ty::ty_evec(_, ty::vstore_slice(r_ptr)) => {
@@ -456,66 +586,22 @@ fn constrain_index(rcx: @mut Rcx,
     }
 }
 
-fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) {
-    /*!
-     *
-     * If `expr` is auto-ref'd (e.g., as part of a borrow), then this
-     * function ensures that the lifetime of the resulting borrowed
-     * ptr includes at least the expression `expr`. */
-
-    debug!("constrain_auto_ref(expr=%s)", rcx.fcx.expr_to_str(expr));
-
-    let adjustment = rcx.fcx.inh.adjustments.find(&expr.id);
-    let region = match adjustment {
-        Some(&@ty::AutoDerefRef(
-            ty::AutoDerefRef {
-                autoref: Some(ref auto_ref), _})) => {
-            auto_ref.region
-        }
-        _ => { return; }
-    };
-
-    let tcx = rcx.fcx.tcx();
-    let encl_region = tcx.region_maps.encl_region(expr.id);
-    match rcx.fcx.mk_subr(true, expr.span, encl_region, region) {
-        result::Ok(()) => {}
-        result::Err(_) => {
-            // In practice, this cannot happen: `region` is always a
-            // region variable, and constraints on region variables
-            // are collected and then resolved later.  However, I
-            // included the span_err() here (rather than, say,
-            // span_bug()) because it seemed more future-proof: if,
-            // for some reason, the code were to change so that in
-            // some cases `region` is not a region variable, then
-            // reporting an error would be the correct path.
-            tcx.sess.span_err(
-                expr.span,
-                ~"lifetime of borrowed pointer does not include \
-                  the expression being borrowed");
-            note_and_explain_region(
-                tcx,
-                ~"lifetime of the borrowed pointer is",
-                region,
-                ~"");
-            rcx.errors_reported += 1;
-        }
-    }
-}
-
-fn constrain_free_variables(
-    rcx: @mut Rcx,
-    region: ty::Region,
-    expr: @ast::expr) {
+fn constrain_free_variables(rcx: @mut Rcx,
+                            region: ty::Region,
+                            expr: @ast::expr) {
     /*!
-     *
      * Make sure that all free variables referenced inside the closure
-     * outlive the closure itself. */
+     * outlive the closure itself.
+     */
 
     let tcx = rcx.fcx.ccx.tcx;
+    debug!("constrain_free_variables(%s, %s)",
+           region.repr(tcx), expr.repr(tcx));
     for get_freevars(tcx, expr.id).each |freevar| {
         debug!("freevar def is %?", freevar.def);
         let def = freevar.def;
         let en_region = encl_region_of_def(rcx.fcx, def);
+        debug!("en_region = %s", en_region.repr(tcx));
         match rcx.fcx.mk_subr(true, freevar.span,
                               region, en_region) {
           result::Ok(()) => {}
@@ -541,9 +627,13 @@ fn constrain_free_variables(
 fn constrain_regions_in_type_of_node(
     rcx: @mut Rcx,
     id: ast::node_id,
-    encl_region: ty::Region,
+    minimum_lifetime: ty::Region,
     span: span) -> bool
 {
+    //! Guarantees that any lifetimes which appear in the type of
+    //! the node `id` (after applying adjustments) are valid for at
+    //! least `minimum_lifetime`
+
     let tcx = rcx.fcx.tcx();
 
     // Try to resolve the type.  If we encounter an error, then typeck
@@ -553,22 +643,21 @@ fn constrain_regions_in_type_of_node(
     let adjustment = rcx.fcx.inh.adjustments.find(&id);
     let ty = ty::adjust_ty(tcx, span, ty0, adjustment);
     debug!("constrain_regions_in_type_of_node(\
-            ty=%s, ty0=%s, id=%d, encl_region=%?, adjustment=%?)",
+            ty=%s, ty0=%s, id=%d, minimum_lifetime=%?, adjustment=%?)",
            ty_to_str(tcx, ty), ty_to_str(tcx, ty0),
-           id, encl_region, adjustment);
-    constrain_regions_in_type(rcx, encl_region, span, ty)
+           id, minimum_lifetime, adjustment);
+    constrain_regions_in_type(rcx, minimum_lifetime, span, ty)
 }
 
 fn constrain_regions_in_type(
     rcx: @mut Rcx,
-    encl_region: ty::Region,
+    minimum_lifetime: ty::Region,
     span: span,
     ty: ty::t) -> bool
 {
     /*!
-     *
      * Requires that any regions which appear in `ty` must be
-     * superregions of `encl_region`.  Also enforces the constraint
+     * superregions of `minimum_lifetime`.  Also enforces the constraint
      * that given a pointer type `&'r T`, T must not contain regions
      * that outlive 'r, as well as analogous constraints for other
      * lifetime'd types.
@@ -583,11 +672,11 @@ fn constrain_regions_in_type(
     let e = rcx.errors_reported;
     let tcx = rcx.fcx.ccx.tcx;
 
-    debug!("constrain_regions_in_type(encl_region=%s, ty=%s)",
-           region_to_str(tcx, encl_region),
+    debug!("constrain_regions_in_type(minimum_lifetime=%s, ty=%s)",
+           region_to_str(tcx, minimum_lifetime),
            ty_to_str(tcx, ty));
 
-    do relate_nested_regions(tcx, Some(encl_region), ty) |r_sub, r_sup| {
+    do relate_nested_regions(tcx, Some(minimum_lifetime), ty) |r_sub, r_sup| {
         debug!("relate(r_sub=%s, r_sup=%s)",
                region_to_str(tcx, r_sub),
                region_to_str(tcx, r_sup));
@@ -595,12 +684,12 @@ fn constrain_regions_in_type(
         if r_sup.is_bound() || r_sub.is_bound() {
             // a bound region is one which appears inside an fn type.
             // (e.g., the `&` in `fn(&T)`).  Such regions need not be
-            // constrained by `encl_region` as they are placeholders
+            // constrained by `minimum_lifetime` as they are placeholders
             // for regions that are as-yet-unknown.
         } else {
             match rcx.fcx.mk_subr(true, span, r_sub, r_sup) {
                 result::Err(_) => {
-                    if r_sub == encl_region {
+                    if r_sub == minimum_lifetime {
                         tcx.sess.span_err(
                             span,
                             fmt!("reference is not valid outside of its lifetime"));
@@ -639,7 +728,6 @@ fn constrain_regions_in_type(
 
 pub mod guarantor {
     /*!
-     *
      * The routines in this module are aiming to deal with the case
      * where a the contents of a borrowed pointer are re-borrowed.
      * Imagine you have a borrowed pointer `b` with lifetime L1 and
@@ -686,6 +774,7 @@ pub mod guarantor {
      */
 
     use middle::typeck::check::regionck::{Rcx, infallibly_mk_subr};
+    use middle::typeck::check::regionck::mk_subregion_due_to_derefence;
     use middle::ty;
     use syntax::ast;
     use syntax::codemap::span;
@@ -693,14 +782,12 @@ pub mod guarantor {
 
     pub fn for_addr_of(rcx: @mut Rcx, expr: @ast::expr, base: @ast::expr) {
         /*!
-         *
          * Computes the guarantor for an expression `&base` and then
          * ensures that the lifetime of the resulting pointer is linked
          * to the lifetime of its guarantor (if any).
          */
 
         debug!("guarantor::for_addr_of(base=%s)", rcx.fcx.expr_to_str(base));
-        let _i = ::util::common::indenter();
 
         let guarantor = guarantor(rcx, base);
         link(rcx, expr.span, expr.id, guarantor);
@@ -708,13 +795,14 @@ pub mod guarantor {
 
     pub fn for_match(rcx: @mut Rcx, discr: @ast::expr, arms: &[ast::arm]) {
         /*!
-         *
          * Computes the guarantors for any ref bindings in a match and
          * then ensures that the lifetime of the resulting pointer is
          * linked to the lifetime of its guarantor (if any).
          */
 
+        debug!("regionck::for_match()");
         let discr_guarantor = guarantor(rcx, discr);
+        debug!("discr_guarantor=%s", discr_guarantor.repr(rcx.tcx()));
         for arms.each |arm| {
             for arm.pats.each |pat| {
                 link_ref_bindings_in_pat(rcx, *pat, discr_guarantor);
@@ -727,7 +815,6 @@ pub mod guarantor {
                        autoderefs: uint,
                        autoref: &ty::AutoRef) {
         /*!
-         *
          * Computes the guarantor for an expression that has an
          * autoref adjustment and links it to the lifetime of the
          * autoref.  This is only important when auto re-borrowing
@@ -736,30 +823,30 @@ pub mod guarantor {
 
         debug!("guarantor::for_autoref(expr=%s, autoref=%?)",
                rcx.fcx.expr_to_str(expr), autoref);
-        let _i = ::util::common::indenter();
 
         let mut expr_ct = categorize_unadjusted(rcx, expr);
         debug!("    unadjusted cat=%?", expr_ct.cat);
         expr_ct = apply_autoderefs(
             rcx, expr, autoderefs, expr_ct);
 
-        match autoref.kind {
-            ty::AutoPtr => {
+        match *autoref {
+            ty::AutoPtr(r, _) => {
                 // In this case, we are implicitly adding an `&`.
-                maybe_make_subregion(rcx, expr, autoref.region,
-                                     expr_ct.cat.guarantor);
+                maybe_make_subregion(rcx, expr, r, expr_ct.cat.guarantor);
             }
 
-            ty::AutoBorrowVec |
-            ty::AutoBorrowVecRef |
-            ty::AutoBorrowFn => {
+            ty::AutoBorrowVec(r, _) |
+            ty::AutoBorrowVecRef(r, _) |
+            ty::AutoBorrowFn(r) => {
                 // In each of these cases, what is being borrowed is
                 // not the (autoderef'd) expr itself but rather the
                 // contents of the autoderef'd expression (i.e., what
                 // the pointer points at).
-                maybe_make_subregion(rcx, expr, autoref.region,
+                maybe_make_subregion(rcx, expr, r,
                                      guarantor_of_deref(&expr_ct.cat));
             }
+
+            ty::AutoUnsafe(_) => {}
         }
 
         fn maybe_make_subregion(
@@ -774,6 +861,28 @@ pub mod guarantor {
         }
     }
 
+    pub fn for_by_ref(rcx: @mut Rcx,
+                      expr: @ast::expr,
+                      callee_scope: ast::node_id) {
+        /*!
+         * Computes the guarantor for cases where the `expr` is
+         * being passed by implicit reference and must outlive
+         * `callee_scope`.
+         */
+
+        let tcx = rcx.tcx();
+        debug!("guarantor::for_by_ref(expr=%s, callee_scope=%?)",
+               expr.repr(tcx), callee_scope);
+        let mut expr_cat = categorize(rcx, expr);
+        debug!("guarantor::for_by_ref(expr=%?, callee_scope=%?) category=%?",
+               expr.id, callee_scope, expr_cat);
+        let minimum_lifetime = ty::re_scope(callee_scope);
+        for expr_cat.guarantor.each |guarantor| {
+            mk_subregion_due_to_derefence(rcx, expr.span,
+                                          minimum_lifetime, *guarantor);
+        }
+    }
+
     fn link(
         rcx: @mut Rcx,
         span: span,
@@ -907,7 +1016,6 @@ pub mod guarantor {
 
     fn categorize(rcx: @mut Rcx, expr: @ast::expr) -> ExprCategorization {
         debug!("categorize(expr=%s)", rcx.fcx.expr_to_str(expr));
-        let _i = ::util::common::indenter();
 
         let mut expr_ct = categorize_unadjusted(rcx, expr);
         debug!("before adjustments, cat=%?", expr_ct.cat);
@@ -928,12 +1036,24 @@ pub mod guarantor {
                 expr_ct = apply_autoderefs(
                     rcx, expr, adjustment.autoderefs, expr_ct);
 
-                for adjustment.autoref.each |autoref| {
-                    // If there is an autoref, then the result of this
-                    // expression will be some sort of borrowed pointer.
-                    expr_ct.cat.guarantor = None;
-                    expr_ct.cat.pointer = BorrowedPointer(autoref.region);
-                    debug!("autoref, cat=%?", expr_ct.cat);
+                match adjustment.autoref {
+                    None => {
+                    }
+                    Some(ty::AutoUnsafe(_)) => {
+                        expr_ct.cat.guarantor = None;
+                        expr_ct.cat.pointer = OtherPointer;
+                        debug!("autoref, cat=%?", expr_ct.cat);
+                    }
+                    Some(ty::AutoPtr(r, _)) |
+                    Some(ty::AutoBorrowVec(r, _)) |
+                    Some(ty::AutoBorrowVecRef(r, _)) |
+                    Some(ty::AutoBorrowFn(r)) => {
+                        // If there is an autoref, then the result of this
+                        // expression will be some sort of borrowed pointer.
+                        expr_ct.cat.guarantor = None;
+                        expr_ct.cat.pointer = BorrowedPointer(r);
+                        debug!("autoref, cat=%?", expr_ct.cat);
+                    }
                 }
             }
 
@@ -948,7 +1068,6 @@ pub mod guarantor {
                              expr: @ast::expr)
                           -> ExprCategorizationType {
         debug!("categorize_unadjusted(expr=%s)", rcx.fcx.expr_to_str(expr));
-        let _i = ::util::common::indenter();
 
         let guarantor = {
             if rcx.fcx.inh.method_map.contains_key(&expr.id) {
@@ -1053,7 +1172,6 @@ pub mod guarantor {
 
         debug!("link_ref_bindings_in_pat(pat=%s, guarantor=%?)",
                rcx.fcx.pat_to_str(pat), guarantor);
-        let _i = ::util::common::indenter();
 
         match pat.node {
             ast::pat_wild => {}
@@ -1069,7 +1187,10 @@ pub mod guarantor {
                     link_ref_bindings_in_pat(rcx, *p, guarantor);
                 }
             }
-            ast::pat_enum(*) => {}
+            ast::pat_enum(_, None) => {}
+            ast::pat_enum(_, Some(ref pats)) => {
+                link_ref_bindings_in_pats(rcx, pats, guarantor);
+            }
             ast::pat_struct(_, ref fpats, _) => {
                 for fpats.each |fpat| {
                     link_ref_bindings_in_pat(rcx, fpat.pat, guarantor);
diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs
index f293893bc13..cfbd012b7b7 100644
--- a/src/librustc/middle/typeck/check/regionmanip.rs
+++ b/src/librustc/middle/typeck/check/regionmanip.rs
@@ -87,7 +87,7 @@ pub fn replace_bound_regions_in_fn_sig(
                       to_r: &fn(ty::bound_region) -> ty::Region,
                       r: ty::Region) -> isr_alist {
             match r {
-              ty::re_free(*) | ty::re_static | ty::re_scope(_) |
+              ty::re_empty | ty::re_free(*) | ty::re_static | ty::re_scope(_) |
               ty::re_infer(_) => {
                 isr
               }
@@ -153,6 +153,7 @@ pub fn replace_bound_regions_in_fn_sig(
               }
 
               // Free regions like these just stay the same:
+              ty::re_empty |
               ty::re_static |
               ty::re_scope(_) |
               ty::re_free(*) |
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index d6b09d1e7f4..b7713eaa2fd 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -134,23 +134,22 @@ fn resolve_type_vars_for_node(wbcx: @mut WbCtxt, sp: span, id: ast::node_id)
         }
 
         Some(&@ty::AutoDerefRef(adj)) => {
-            let resolved_autoref = match adj.autoref {
-                Some(ref autoref) => {
-                    match resolve_region(fcx.infcx(), autoref.region,
-                                         resolve_all | force_all) {
-                        Err(e) => {
-                            // This should not, I think, happen.
-                            fcx.ccx.tcx.sess.span_err(
-                                sp, fmt!("cannot resolve scope of borrow: %s",
-                                         infer::fixup_err_to_str(e)));
-                            Some(*autoref)
-                        }
-                        Ok(r) => {
-                            Some(ty::AutoRef {region: r, ..*autoref})
-                        }
+            let fixup_region = |r| {
+                match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
+                    Ok(r1) => r1,
+                    Err(e) => {
+                        // This should not, I think, happen.
+                        fcx.ccx.tcx.sess.span_err(
+                            sp, fmt!("cannot resolve scope of borrow: %s",
+                                     infer::fixup_err_to_str(e)));
+                        r
                     }
                 }
-                None => None
+            };
+
+            let resolved_autoref = match adj.autoref {
+                None => None,
+                Some(ref r) => Some(r.map_region(fixup_region))
             };
 
             let resolved_adj = @ty::AutoDerefRef(ty::AutoDerefRef {
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index 05b2f6f577b..573e4bd5790 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -393,7 +393,7 @@ pub impl CoherenceChecker {
 
             let pmm = self.crate_context.tcx.provided_methods;
             match pmm.find(&local_def(impl_id)) {
-                Some(mis) => {
+                Some(&mis) => {
                     // If the trait already has an entry in the
                     // provided_methods_map, we just need to add this
                     // method to that entry.
@@ -426,8 +426,8 @@ pub impl CoherenceChecker {
                 self.crate_context.coherence_info.inherent_methods
                     .insert(base_def_id, implementation_list);
             }
-            Some(existing_implementation_list) => {
-                implementation_list = *existing_implementation_list;
+            Some(&existing_implementation_list) => {
+                implementation_list = existing_implementation_list;
             }
         }
 
@@ -443,8 +443,8 @@ pub impl CoherenceChecker {
                 self.crate_context.coherence_info.extension_methods
                     .insert(trait_id, implementation_list);
             }
-            Some(existing_implementation_list) => {
-                implementation_list = *existing_implementation_list;
+            Some(&existing_implementation_list) => {
+                implementation_list = existing_implementation_list;
             }
         }
 
@@ -507,7 +507,7 @@ pub impl CoherenceChecker {
                 m.insert(self_t, the_impl);
                 self.crate_context.tcx.trait_impls.insert(trait_t, m);
             }
-            Some(m) => {
+            Some(&m) => {
                 m.insert(self_t, the_impl);
             }
         }
diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs
index dcd1c861540..3620b609edf 100644
--- a/src/librustc/middle/typeck/infer/coercion.rs
+++ b/src/librustc/middle/typeck/infer/coercion.rs
@@ -65,7 +65,7 @@ we may want to adjust precisely when coercions occur.
 */
 
 use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowFn};
-use middle::ty::{AutoDerefRef, AutoRef};
+use middle::ty::{AutoDerefRef};
 use middle::ty::{vstore_slice, vstore_box, vstore_uniq};
 use middle::ty::{mt};
 use middle::ty;
@@ -120,9 +120,9 @@ pub impl Coerce {
                 };
             }
 
-            ty::ty_ptr(_) => {
+            ty::ty_ptr(mt_b) => {
                 return do self.unpack_actual_value(a) |sty_a| {
-                    self.coerce_unsafe_ptr(a, sty_a, b)
+                    self.coerce_unsafe_ptr(a, sty_a, b, mt_b)
                 };
             }
 
@@ -205,11 +205,7 @@ pub impl Coerce {
         if_ok!(sub.tys(a_borrowed, b));
         Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 1,
-            autoref: Some(AutoRef {
-                kind: AutoPtr,
-                region: r_borrow,
-                mutbl: mt_b.mutbl
-            })
+            autoref: Some(AutoPtr(r_borrow, mt_b.mutbl))
         })))
     }
 
@@ -235,11 +231,7 @@ pub impl Coerce {
         if_ok!(self.subtype(a_borrowed, b));
         Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
-            autoref: Some(AutoRef {
-                kind: AutoBorrowVec,
-                region: r_a,
-                mutbl: m_imm
-            })
+            autoref: Some(AutoBorrowVec(r_a, m_imm))
         })))
     }
 
@@ -268,11 +260,7 @@ pub impl Coerce {
         if_ok!(sub.tys(a_borrowed, b));
         Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
-            autoref: Some(AutoRef {
-                kind: AutoBorrowVec,
-                region: r_borrow,
-                mutbl: mt_b.mutbl
-            })
+            autoref: Some(AutoBorrowVec(r_borrow, mt_b.mutbl))
         })))
     }
 
@@ -308,11 +296,7 @@ pub impl Coerce {
         if_ok!(self.subtype(a_borrowed, b));
         Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
-            autoref: Some(AutoRef {
-                kind: AutoBorrowFn,
-                region: r_borrow,
-                mutbl: m_imm
-            })
+            autoref: Some(AutoBorrowFn(r_borrow))
         })))
     }
 
@@ -363,7 +347,8 @@ pub impl Coerce {
     fn coerce_unsafe_ptr(&self,
                          a: ty::t,
                          sty_a: &ty::sty,
-                         b: ty::t) -> CoerceResult
+                         b: ty::t,
+                         mt_b: ty::mt) -> CoerceResult
     {
         debug!("coerce_unsafe_ptr(a=%s, sty_a=%?, b=%s)",
                a.inf_str(self.infcx), sty_a,
@@ -376,10 +361,17 @@ pub impl Coerce {
             }
         };
 
-        // borrowed pointers and unsafe pointers have the same
-        // representation, so just check that the types which they
-        // point at are compatible:
+        // check that the types which they point at are compatible
         let a_unsafe = ty::mk_ptr(self.infcx.tcx, mt_a);
-        self.subtype(a_unsafe, b)
+        if_ok!(self.subtype(a_unsafe, b));
+
+        // although borrowed ptrs and unsafe ptrs have the same
+        // representation, we still register an AutoDerefRef so that
+        // regionck knows that that the region for `a` must be valid
+        // here
+        Ok(Some(@AutoDerefRef(AutoDerefRef {
+            autoderefs: 1,
+            autoref: Some(ty::AutoUnsafe(mt_b.mutbl))
+        })))
     }
 }
diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs
index 7b5a93d4cad..4491b04b382 100644
--- a/src/librustc/middle/typeck/infer/mod.rs
+++ b/src/librustc/middle/typeck/infer/mod.rs
@@ -339,7 +339,7 @@ pub fn fixup_err_to_str(f: fixup_err) -> ~str {
 
 fn new_ValsAndBindings<V:Copy,T:Copy>() -> ValsAndBindings<V, T> {
     ValsAndBindings {
-        vals: @mut SmallIntMap::new(),
+        vals: SmallIntMap::new(),
         bindings: ~[]
     }
 }
@@ -469,28 +469,6 @@ pub fn resolve_region(cx: @mut InferCtxt, r: ty::Region, modes: uint)
     resolver.resolve_region_chk(r)
 }
 
-/*
-fn resolve_borrowings(cx: @mut InferCtxt) {
-    for cx.borrowings.each |item| {
-        match resolve_region(cx, item.scope, resolve_all|force_all) {
-          Ok(region) => {
-            debug!("borrowing for expr %d resolved to region %?, mutbl %?",
-                   item.expr_id, region, item.mutbl);
-            cx.tcx.borrowings.insert(
-                item.expr_id, {region: region, mutbl: item.mutbl});
-          }
-
-          Err(e) => {
-            let str = fixup_err_to_str(e);
-            cx.tcx.sess.span_err(
-                item.span,
-                fmt!("could not resolve lifetime for borrow: %s", str));
-          }
-        }
-    }
-}
-*/
-
 trait then {
     fn then<T:Copy>(&self, f: &fn() -> Result<T,ty::type_err>)
         -> Result<T,ty::type_err>;
@@ -554,7 +532,8 @@ struct Snapshot {
 }
 
 pub impl InferCtxt {
-    fn combine_fields(@mut self, a_is_expected: bool,
+    fn combine_fields(@mut self,
+                      a_is_expected: bool,
                       span: span) -> CombineFields {
         CombineFields {infcx: self,
                        a_is_expected: a_is_expected,
@@ -565,25 +544,24 @@ pub impl InferCtxt {
         Sub(self.combine_fields(a_is_expected, span))
     }
 
-    fn in_snapshot(@mut self) -> bool {
+    fn in_snapshot(&self) -> bool {
         self.region_vars.in_snapshot()
     }
 
-    fn start_snapshot(@mut self) -> Snapshot {
-        let this = &mut *self;
+    fn start_snapshot(&mut self) -> Snapshot {
         Snapshot {
             ty_var_bindings_len:
-                this.ty_var_bindings.bindings.len(),
+                self.ty_var_bindings.bindings.len(),
             int_var_bindings_len:
-                this.int_var_bindings.bindings.len(),
+                self.int_var_bindings.bindings.len(),
             float_var_bindings_len:
-                this.float_var_bindings.bindings.len(),
+                self.float_var_bindings.bindings.len(),
             region_vars_snapshot:
-                this.region_vars.start_snapshot(),
+                self.region_vars.start_snapshot(),
         }
     }
 
-    fn rollback_to(@mut self, snapshot: &Snapshot) {
+    fn rollback_to(&mut self, snapshot: &Snapshot) {
         debug!("rollback!");
         rollback_to(&mut self.ty_var_bindings, snapshot.ty_var_bindings_len);
 
@@ -596,7 +574,7 @@ pub impl InferCtxt {
     }
 
     /// Execute `f` and commit the bindings if successful
-    fn commit<T,E>(@mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
+    fn commit<T,E>(&mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
         assert!(!self.in_snapshot());
 
         debug!("commit()");
@@ -611,7 +589,7 @@ pub impl InferCtxt {
     }
 
     /// Execute `f`, unroll bindings on failure
-    fn try<T,E>(@mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
+    fn try<T,E>(&mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
         debug!("try()");
         do indent {
             let snapshot = self.start_snapshot();
@@ -625,7 +603,7 @@ pub impl InferCtxt {
     }
 
     /// Execute `f` then unroll any bindings it creates
-    fn probe<T,E>(@mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
+    fn probe<T,E>(&mut self, f: &fn() -> Result<T,E>) -> Result<T,E> {
         debug!("probe()");
         do indent {
             let snapshot = self.start_snapshot();
@@ -647,45 +625,47 @@ fn next_simple_var<V:Copy,T:Copy>(
 }
 
 pub impl InferCtxt {
-    fn next_ty_var_id(@mut self) -> TyVid {
+    fn next_ty_var_id(&mut self) -> TyVid {
         let id = self.ty_var_counter;
         self.ty_var_counter += 1;
-        let vals = self.ty_var_bindings.vals;
-        vals.insert(id, Root(Bounds { lb: None, ub: None }, 0u));
+        {
+            let vals = &mut self.ty_var_bindings.vals;
+            vals.insert(id, Root(Bounds { lb: None, ub: None }, 0u));
+        }
         return TyVid(id);
     }
 
-    fn next_ty_var(@mut self) -> ty::t {
+    fn next_ty_var(&mut self) -> ty::t {
         ty::mk_var(self.tcx, self.next_ty_var_id())
     }
 
-    fn next_ty_vars(@mut self, n: uint) -> ~[ty::t] {
+    fn next_ty_vars(&mut self, n: uint) -> ~[ty::t] {
         vec::from_fn(n, |_i| self.next_ty_var())
     }
 
-    fn next_int_var_id(@mut self) -> IntVid {
+    fn next_int_var_id(&mut self) -> IntVid {
         IntVid(next_simple_var(&mut self.int_var_counter,
                                &mut self.int_var_bindings))
     }
 
-    fn next_int_var(@mut self) -> ty::t {
+    fn next_int_var(&mut self) -> ty::t {
         ty::mk_int_var(self.tcx, self.next_int_var_id())
     }
 
-    fn next_float_var_id(@mut self) -> FloatVid {
+    fn next_float_var_id(&mut self) -> FloatVid {
         FloatVid(next_simple_var(&mut self.float_var_counter,
                                  &mut self.float_var_bindings))
     }
 
-    fn next_float_var(@mut self) -> ty::t {
+    fn next_float_var(&mut self) -> ty::t {
         ty::mk_float_var(self.tcx, self.next_float_var_id())
     }
 
-    fn next_region_var_nb(@mut self, span: span) -> ty::Region {
+    fn next_region_var_nb(&mut self, span: span) -> ty::Region {
         ty::re_infer(ty::ReVar(self.region_vars.new_region_var(span)))
     }
 
-    fn next_region_var_with_lb(@mut self, span: span,
+    fn next_region_var_with_lb(&mut self, span: span,
                                lb_region: ty::Region) -> ty::Region {
         let region_var = self.next_region_var_nb(span);
 
@@ -697,12 +677,12 @@ pub impl InferCtxt {
         return region_var;
     }
 
-    fn next_region_var(@mut self, span: span, scope_id: ast::node_id)
+    fn next_region_var(&mut self, span: span, scope_id: ast::node_id)
                       -> ty::Region {
         self.next_region_var_with_lb(span, ty::re_scope(scope_id))
     }
 
-    fn resolve_regions(@mut self) {
+    fn resolve_regions(&mut self) {
         self.region_vars.resolve_regions();
     }
 
@@ -722,7 +702,6 @@ pub impl InferCtxt {
           result::Err(_) => typ
         }
     }
-
     fn resolve_type_vars_in_trait_ref_if_possible(@mut self,
                                                   trait_ref: &ty::TraitRef)
         -> ty::TraitRef
@@ -786,7 +765,7 @@ pub impl InferCtxt {
         self.type_error_message(sp, mk_msg, a, Some(err));
     }
 
-    fn replace_bound_regions_with_fresh_regions(@mut self,
+    fn replace_bound_regions_with_fresh_regions(&mut self,
             span: span,
             fsig: &ty::FnSig)
          -> (ty::FnSig, isr_alist) {
@@ -806,7 +785,7 @@ pub impl InferCtxt {
     }
 
     fn fold_regions_in_sig(
-        @mut self,
+        &mut self,
         fn_sig: &ty::FnSig,
         fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnSig
     {
diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs
index e12a3f2e975..0761ad5c7b8 100644
--- a/src/librustc/middle/typeck/infer/region_inference.rs
+++ b/src/librustc/middle/typeck/infer/region_inference.rs
@@ -24,7 +24,7 @@ it's worth spending more time on a more involved analysis.  Moreover,
 regions are a simpler case than types: they don't have aggregate
 structure, for example.
 
-Unlike normal type inference, which is similar in spirit H-M and thus
+Unlike normal type inference, which is similar in spirit to H-M and thus
 works progressively, the region type inference works by accumulating
 constraints over the course of a function.  Finally, at the end of
 processing a function, we process and solve the constraints all at
@@ -130,7 +130,7 @@ of these variables can effectively be unified into a single variable.
 Once SCCs are removed, we are left with a DAG.  At this point, we can
 walk the DAG in toplogical order once to compute the expanding nodes,
 and again in reverse topological order to compute the contracting
-nodes.The main reason I did not write it this way is that I did not
+nodes. The main reason I did not write it this way is that I did not
 feel like implementing the SCC and toplogical sort algorithms at the
 moment.
 
@@ -538,7 +538,7 @@ more convincing in the future.
 
 use middle::ty;
 use middle::ty::{FreeRegion, Region, RegionVid};
-use middle::ty::{re_static, re_infer, re_free, re_bound};
+use middle::ty::{re_empty, re_static, re_infer, re_free, re_bound};
 use middle::ty::{re_scope, ReVar, ReSkolemized, br_fresh};
 use middle::typeck::infer::cres;
 use util::common::indenter;
@@ -547,6 +547,9 @@ use util::ppaux::note_and_explain_region;
 use core::cell::{Cell, empty_cell};
 use core::hashmap::{HashMap, HashSet};
 use core::to_bytes;
+use core::uint;
+use core::vec;
+use core;
 use syntax::codemap::span;
 use syntax::ast;
 
@@ -572,18 +575,12 @@ impl to_bytes::IterBytes for Constraint {
     }
 }
 
-#[deriving(Eq)]
+#[deriving(Eq, IterBytes)]
 struct TwoRegions {
     a: Region,
     b: Region,
 }
 
-impl to_bytes::IterBytes for TwoRegions {
-    fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) {
-        to_bytes::iter_bytes_2(&self.a, &self.b, lsb0, f)
-    }
-}
-
 enum UndoLogEntry {
     Snapshot,
     AddVar(RegionVid),
@@ -637,7 +634,7 @@ pub fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
 }
 
 pub impl RegionVarBindings {
-    fn in_snapshot(&mut self) -> bool {
+    fn in_snapshot(&self) -> bool {
         self.undo_log.len() > 0
     }
 
@@ -832,7 +829,6 @@ pub impl RegionVarBindings {
     }
 
     fn resolve_var(&mut self, rid: RegionVid) -> ty::Region {
-        debug!("RegionVarBindings: resolve_var(%?=%u)", rid, rid.to_uint());
         if self.values.is_empty() {
             self.tcx.sess.span_bug(
                 self.var_spans[rid.to_uint()],
@@ -841,29 +837,14 @@ pub impl RegionVarBindings {
         }
 
         let v = self.values.with_ref(|values| values[rid.to_uint()]);
+        debug!("RegionVarBindings: resolve_var(%?=%u)=%?",
+               rid, rid.to_uint(), v);
         match v {
             Value(r) => r,
 
             NoValue => {
-                // No constraints, report an error.  It is plausible
-                // that we could select an arbitrary region here
-                // instead.  At the moment I am not doing this because
-                // this generally masks bugs in the inference
-                // algorithm, and given our syntax one cannot create
-                // generally create a lifetime variable that isn't
-                // used in some type, and hence all lifetime variables
-                // should ultimately have some bounds.
-
-                self.tcx.sess.span_err(
-                    self.var_spans[rid.to_uint()],
-                    fmt!("Unconstrained region variable #%u", rid.to_uint()));
-
-                // Touch of a hack: to suppress duplicate messages,
-                // replace the NoValue entry with ErrorValue.
-                let mut values = self.values.take();
-                values[rid.to_uint()] = ErrorValue;
-                self.values.put_back(values);
-                re_static
+                // No constraints, return ty::re_empty
+                re_empty
             }
 
             ErrorValue => {
@@ -1031,6 +1012,10 @@ priv impl RegionVarBindings {
             re_static // nothing lives longer than static
           }
 
+          (re_empty, r) | (r, re_empty) => {
+            r // everything lives longer than empty
+          }
+
           (re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => {
             self.tcx.sess.span_bug(
                 self.var_spans[v_id.to_uint()],
@@ -1127,6 +1112,11 @@ priv impl RegionVarBindings {
                 Ok(r)
             }
 
+            (re_empty, _) | (_, re_empty) => {
+                // nothing lives shorter than everything else
+                Ok(re_empty)
+            }
+
             (re_infer(ReVar(v_id)), _) |
             (_, re_infer(ReVar(v_id))) => {
                 self.tcx.sess.span_bug(
@@ -1266,8 +1256,6 @@ struct SpannedRegion {
     span: span,
 }
 
-type TwoRegionsMap = HashSet<TwoRegions>;
-
 pub impl RegionVarBindings {
     fn infer_variable_values(&mut self) -> ~[GraphNodeValue] {
         let mut graph = self.construct_graph();
@@ -1329,11 +1317,15 @@ pub impl RegionVarBindings {
                        node_id: RegionVid,
                        edge_dir: Direction,
                        edge_idx: uint) {
+            //! Insert edge `edge_idx` on the link list of edges in direction
+            //! `edge_dir` for the node `node_id`
             let edge_dir = edge_dir as uint;
-            graph.edges[edge_idx].next_edge[edge_dir] =
-                graph.nodes[node_id.to_uint()].head_edge[edge_dir];
-            graph.nodes[node_id.to_uint()].head_edge[edge_dir] =
-                edge_idx;
+            assert_eq!(graph.edges[edge_idx].next_edge[edge_dir],
+                       uint::max_value);
+            let n = node_id.to_uint();
+            let prev_head = graph.nodes[n].head_edge[edge_dir];
+            graph.edges[edge_idx].next_edge[edge_dir] = prev_head;
+            graph.nodes[n].head_edge[edge_dir] = edge_idx;
         }
     }
 
@@ -1484,6 +1476,8 @@ pub impl RegionVarBindings {
                     }
                 }
                 Err(_) => {
+                    debug!("Setting %? to ErrorValue: no glb of %?, %?",
+                           a_vid, a_region, b_region);
                     a_node.value = ErrorValue;
                     false
                 }
@@ -1495,7 +1489,21 @@ pub impl RegionVarBindings {
         &mut self,
         graph: &Graph) -> ~[GraphNodeValue]
     {
-        let mut dup_map = HashSet::new();
+        debug!("extract_values_and_report_conflicts()");
+
+        // This is the best way that I have found to suppress
+        // duplicate and related errors. Basically we keep a set of
+        // flags for every node. Whenever an error occurs, we will
+        // walk some portion of the graph looking to find pairs of
+        // conflicting regions to report to the user. As we walk, we
+        // trip the flags from false to true, and if we find that
+        // we've already reported an error involving any particular
+        // node we just stop and don't report the current error.  The
+        // idea is to report errors that derive from independent
+        // regions of the graph, but not those that derive from
+        // overlapping locations.
+        let mut dup_vec = graph.nodes.map(|_| uint::max_value);
+
         graph.nodes.mapi(|idx, node| {
             match node.value {
                 Value(_) => {
@@ -1530,15 +1538,16 @@ pub impl RegionVarBindings {
                        that is not used is not a problem, so if this rule
                        starts to create problems we'll have to revisit
                        this portion of the code and think hard about it. =) */
+
                     let node_vid = RegionVid { id: idx };
                     match node.classification {
                         Expanding => {
                             self.report_error_for_expanding_node(
-                                graph, &mut dup_map, node_vid);
+                                graph, dup_vec, node_vid);
                         }
                         Contracting => {
                             self.report_error_for_contracting_node(
-                                graph, &mut dup_map, node_vid);
+                                graph, dup_vec, node_vid);
                         }
                     }
                 }
@@ -1548,38 +1557,26 @@ pub impl RegionVarBindings {
         })
     }
 
-    // Used to suppress reporting the same basic error over and over
-    fn is_reported(&mut self,
-                   dup_map: &mut TwoRegionsMap,
-                   r_a: Region,
-                   r_b: Region)
-                -> bool {
-        let key = TwoRegions { a: r_a, b: r_b };
-        !dup_map.insert(key)
-    }
-
     fn report_error_for_expanding_node(&mut self,
                                        graph: &Graph,
-                                       dup_map: &mut TwoRegionsMap,
+                                       dup_vec: &mut [uint],
                                        node_idx: RegionVid) {
         // Errors in expanding nodes result from a lower-bound that is
         // not contained by an upper-bound.
-        let lower_bounds =
-            self.collect_concrete_regions(graph, node_idx, Incoming);
-        let upper_bounds =
-            self.collect_concrete_regions(graph, node_idx, Outgoing);
+        let (lower_bounds, lower_dup) =
+            self.collect_concrete_regions(graph, node_idx, Incoming, dup_vec);
+        let (upper_bounds, upper_dup) =
+            self.collect_concrete_regions(graph, node_idx, Outgoing, dup_vec);
+
+        if lower_dup || upper_dup {
+            return;
+        }
 
         for vec::each(lower_bounds) |lower_bound| {
             for vec::each(upper_bounds) |upper_bound| {
                 if !self.is_subregion_of(lower_bound.region,
                                          upper_bound.region) {
 
-                    if self.is_reported(dup_map,
-                                        lower_bound.region,
-                                        upper_bound.region) {
-                        return;
-                    }
-
                     self.tcx.sess.span_err(
                         self.var_spans[node_idx.to_uint()],
                         fmt!("cannot infer an appropriate lifetime \
@@ -1609,16 +1606,28 @@ pub impl RegionVarBindings {
                 }
             }
         }
+
+        self.tcx.sess.span_bug(
+            self.var_spans[node_idx.to_uint()],
+            fmt!("report_error_for_expanding_node() could not find error \
+                  for var %?, lower_bounds=%s, upper_bounds=%s",
+                 node_idx,
+                 lower_bounds.map(|x| x.region).repr(self.tcx),
+                 upper_bounds.map(|x| x.region).repr(self.tcx)));
     }
 
     fn report_error_for_contracting_node(&mut self,
                                          graph: &Graph,
-                                         dup_map: &mut TwoRegionsMap,
+                                         dup_vec: &mut [uint],
                                          node_idx: RegionVid) {
         // Errors in contracting nodes result from two upper-bounds
         // that have no intersection.
-        let upper_bounds = self.collect_concrete_regions(graph, node_idx,
-                                                         Outgoing);
+        let (upper_bounds, dup_found) =
+            self.collect_concrete_regions(graph, node_idx, Outgoing, dup_vec);
+
+        if dup_found {
+            return;
+        }
 
         for vec::each(upper_bounds) |upper_bound_1| {
             for vec::each(upper_bounds) |upper_bound_2| {
@@ -1627,12 +1636,6 @@ pub impl RegionVarBindings {
                   Ok(_) => {}
                   Err(_) => {
 
-                    if self.is_reported(dup_map,
-                                        upper_bound_1.region,
-                                        upper_bound_2.region) {
-                        return;
-                    }
-
                     self.tcx.sess.span_err(
                         self.var_spans[node_idx.to_uint()],
                         fmt!("cannot infer an appropriate lifetime \
@@ -1663,50 +1666,94 @@ pub impl RegionVarBindings {
                 }
             }
         }
+
+        self.tcx.sess.span_bug(
+            self.var_spans[node_idx.to_uint()],
+            fmt!("report_error_for_contracting_node() could not find error \
+                  for var %?, upper_bounds=%s",
+                 node_idx,
+                 upper_bounds.map(|x| x.region).repr(self.tcx)));
     }
 
     fn collect_concrete_regions(&mut self,
                                 graph: &Graph,
                                 orig_node_idx: RegionVid,
-                                dir: Direction)
-                             -> ~[SpannedRegion] {
-        let mut set = HashSet::new();
-        let mut stack = ~[orig_node_idx];
-        set.insert(orig_node_idx.to_uint());
-        let mut result = ~[];
-        while !vec::is_empty(stack) {
-            let node_idx = stack.pop();
-            for self.each_edge(graph, node_idx, dir) |edge| {
+                                dir: Direction,
+                                dup_vec: &mut [uint])
+                             -> (~[SpannedRegion], bool) {
+        struct WalkState {
+            set: HashSet<RegionVid>,
+            stack: ~[RegionVid],
+            result: ~[SpannedRegion],
+            dup_found: bool
+        }
+        let mut state = WalkState {
+            set: HashSet::new(),
+            stack: ~[orig_node_idx],
+            result: ~[],
+            dup_found: false
+        };
+        state.set.insert(orig_node_idx);
+
+        // to start off the process, walk the source node in the
+        // direction specified
+        process_edges(self, &mut state, graph, orig_node_idx, dir);
+
+        while !state.stack.is_empty() {
+            let node_idx = state.stack.pop();
+            let classification = graph.nodes[node_idx.to_uint()].classification;
+
+            // check whether we've visited this node on some previous walk
+            if dup_vec[node_idx.to_uint()] == uint::max_value {
+                dup_vec[node_idx.to_uint()] = orig_node_idx.to_uint();
+            } else if dup_vec[node_idx.to_uint()] != orig_node_idx.to_uint() {
+                state.dup_found = true;
+            }
+
+            debug!("collect_concrete_regions(orig_node_idx=%?, node_idx=%?, \
+                    classification=%?)",
+                   orig_node_idx, node_idx, classification);
+
+            // figure out the direction from which this node takes its
+            // values, and search for concrete regions etc in that direction
+            let dir = match classification {
+                Expanding => Incoming,
+                Contracting => Outgoing
+            };
+
+            process_edges(self, &mut state, graph, node_idx, dir);
+        }
+
+        let WalkState {result, dup_found, _} = state;
+        return (result, dup_found);
+
+        fn process_edges(self: &mut RegionVarBindings,
+                         state: &mut WalkState,
+                         graph: &Graph,
+                         source_vid: RegionVid,
+                         dir: Direction) {
+            debug!("process_edges(source_vid=%?, dir=%?)", source_vid, dir);
+
+            for self.each_edge(graph, source_vid, dir) |edge| {
                 match edge.constraint {
-                  ConstrainVarSubVar(from_vid, to_vid) => {
-                    let vid = match dir {
-                      Incoming => from_vid,
-                      Outgoing => to_vid
-                    };
-                    if set.insert(vid.to_uint()) {
-                        stack.push(vid);
+                    ConstrainVarSubVar(from_vid, to_vid) => {
+                        let opp_vid =
+                            if from_vid == source_vid {to_vid} else {from_vid};
+                        if state.set.insert(opp_vid) {
+                            state.stack.push(opp_vid);
+                        }
                     }
-                  }
-
-                  ConstrainRegSubVar(region, _) => {
-                    assert!(dir == Incoming);
-                    result.push(SpannedRegion {
-                        region: region,
-                        span: edge.span
-                    });
-                  }
 
-                  ConstrainVarSubReg(_, region) => {
-                    assert!(dir == Outgoing);
-                    result.push(SpannedRegion {
-                        region: region,
-                        span: edge.span
-                    });
-                  }
+                    ConstrainRegSubVar(region, _) |
+                    ConstrainVarSubReg(_, region) => {
+                        state.result.push(SpannedRegion {
+                            region: region,
+                            span: edge.span
+                        });
+                    }
                 }
             }
         }
-        return result;
     }
 
     fn each_edge(&mut self,
diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs
index bc130744224..8db4774322a 100644
--- a/src/librustc/middle/typeck/infer/unify.rs
+++ b/src/librustc/middle/typeck/infer/unify.rs
@@ -23,7 +23,7 @@ pub enum VarValue<V, T> {
 }
 
 pub struct ValsAndBindings<V, T> {
-    vals: @mut SmallIntMap<VarValue<V, T>>,
+    vals: SmallIntMap<VarValue<V, T>>,
     bindings: ~[(V, VarValue<V, T>)],
 }
 
@@ -60,26 +60,25 @@ pub impl InferCtxt {
             vid: V) -> Node<V, T>
         {
             let vid_u = vid.to_uint();
-            match vb.vals.find(&vid_u) {
+            let var_val = match vb.vals.find(&vid_u) {
+                Some(&var_val) => var_val,
                 None => {
                     tcx.sess.bug(fmt!(
                         "failed lookup of vid `%u`", vid_u));
                 }
-                Some(var_val) => {
-                    match *var_val {
-                        Redirect(vid) => {
-                            let node: Node<V,T> = helper(tcx, vb, vid);
-                            if node.root != vid {
-                                // Path compression
-                                vb.vals.insert(vid.to_uint(),
-                                               Redirect(node.root));
-                            }
-                            node
-                        }
-                        Root(ref pt, rk) => {
-                            Node {root: vid, possible_types: *pt, rank: rk}
-                        }
+            };
+            match var_val {
+                Redirect(vid) => {
+                    let node: Node<V,T> = helper(tcx, vb, vid);
+                    if node.root != vid {
+                        // Path compression
+                        vb.vals.insert(vid.to_uint(),
+                                       Redirect(node.root));
                     }
+                    node
+                }
+                Root(pt, rk) => {
+                    Node {root: vid, possible_types: pt, rank: rk}
                 }
             }
         }
@@ -99,8 +98,8 @@ pub impl InferCtxt {
 
         { // FIXME(#4903)---borrow checker is not flow sensitive
             let vb = UnifyVid::appropriate_vals_and_bindings(self);
-            let old_v = vb.vals.get(&vid.to_uint());
-            vb.bindings.push((vid, *old_v));
+            let old_v = { *vb.vals.get(&vid.to_uint()) }; // FIXME(#4903)
+            vb.bindings.push((vid, old_v));
             vb.vals.insert(vid.to_uint(), new_v);
         }
     }
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index aa8c3f8fd1b..d99d87231be 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -65,6 +65,9 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region)
           Some(&ast_map::node_block(ref blk)) => {
             explain_span(cx, "block", blk.span)
           }
+          Some(&ast_map::node_callee_scope(expr)) => {
+              explain_span(cx, "callee", expr.span)
+          }
           Some(&ast_map::node_expr(expr)) => {
             match expr.node {
               ast::expr_call(*) => explain_span(cx, "call", expr.span),
@@ -113,6 +116,8 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region)
 
       re_static => { (~"the static lifetime", None) }
 
+      re_empty => { (~"the empty lifetime", None) }
+
       // I believe these cases should not occur (except when debugging,
       // perhaps)
       re_infer(_) | re_bound(_) => {
@@ -212,7 +217,8 @@ pub fn region_to_str_space(cx: ctxt, prefix: &str, region: Region) -> ~str {
             bound_region_to_str_space(cx, prefix, br)
         }
         re_infer(ReVar(_)) => prefix.to_str(),
-        re_static => fmt!("%s'static ", prefix)
+        re_static => fmt!("%s'static ", prefix),
+        re_empty => fmt!("%s'<empty> ", prefix)
     }
 }
 
@@ -740,6 +746,15 @@ impl Repr for ty::vstore {
     }
 }
 
+impl Repr for ast_map::path_elt {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        match *self {
+            ast_map::path_mod(id) => id.repr(tcx),
+            ast_map::path_name(id) => id.repr(tcx)
+        }
+    }
+}
+
 // Local Variables:
 // mode: rust
 // fill-column: 78;
diff --git a/src/libstd/arc.rs b/src/libstd/arc.rs
index f45fb4e7658..027bf93b481 100644
--- a/src/libstd/arc.rs
+++ b/src/libstd/arc.rs
@@ -419,26 +419,26 @@ pub struct RWReadMode<'self, T> {
 
 pub impl<'self, T:Const + Owned> RWWriteMode<'self, T> {
     /// Access the pre-downgrade RWARC in write mode.
-    fn write<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
+    fn write<U>(&mut self, blk: &fn(x: &mut T) -> U) -> U {
         match *self {
             RWWriteMode {
-                data: ref data,
+                data: &ref mut data,
                 token: ref token,
                 poison: _
             } => {
                 do token.write {
-                    blk(&mut **data)
+                    blk(data)
                 }
             }
         }
     }
     /// Access the pre-downgrade RWARC in write mode with a condvar.
-    fn write_cond<'x, 'c, U>(&self,
+    fn write_cond<'x, 'c, U>(&mut self,
                              blk: &fn(x: &'x mut T, c: &'c Condvar) -> U)
                           -> U {
         match *self {
             RWWriteMode {
-                data: ref data,
+                data: &ref mut data,
                 token: ref token,
                 poison: ref poison
             } => {
@@ -449,7 +449,7 @@ pub impl<'self, T:Const + Owned> RWWriteMode<'self, T> {
                             failed: &mut *poison.failed,
                             cond: cond
                         };
-                        blk(&mut **data, &cvar)
+                        blk(data, &cvar)
                     }
                 }
             }
diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs
index 5314c35419c..d48d7af354b 100644
--- a/src/libstd/bitv.rs
+++ b/src/libstd/bitv.rs
@@ -215,16 +215,16 @@ pub struct Bitv {
     nbits: uint
 }
 
-priv impl Bitv {
+fn die() -> ! {
+    fail!(~"Tried to do operation on bit vectors with different sizes");
+}
 
-    fn die(&self) -> ! {
-        fail!(~"Tried to do operation on bit vectors with different sizes");
-    }
+priv impl Bitv {
 
     #[inline(always)]
     fn do_op(&mut self, op: Op, other: &Bitv) -> bool {
         if self.nbits != other.nbits {
-            self.die();
+            die();
         }
         match self.rep {
           Small(ref mut s) => match other.rep {
@@ -234,10 +234,10 @@ priv impl Bitv {
               Assign     => s.become(*s1,     self.nbits),
               Difference => s.difference(*s1, self.nbits)
             },
-            Big(_) => self.die()
+            Big(_) => die()
           },
           Big(ref mut s) => match other.rep {
-            Small(_) => self.die(),
+            Small(_) => die(),
             Big(ref s1) => match op {
               Union      => s.union(*s1,      self.nbits),
               Intersect  => s.intersect(*s1,  self.nbits),
diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs
index 764152d6812..ec4c025180c 100644
--- a/src/libstd/net_tcp.rs
+++ b/src/libstd/net_tcp.rs
@@ -885,8 +885,8 @@ impl io::Reader for TcpSocketBuf {
                     let ncopy = uint::min(nbuffered, needed);
                     let dst = ptr::mut_offset(
                         vec::raw::to_mut_ptr(buf), count);
-                    let src = ptr::const_offset(
-                        vec::raw::to_const_ptr(self.data.buf),
+                    let src = ptr::offset(
+                        vec::raw::to_ptr(self.data.buf),
                         self.data.buf_off);
                     ptr::copy_memory(dst, src, ncopy);
                     self.data.buf_off += ncopy;
@@ -969,7 +969,7 @@ impl io::Reader for TcpSocketBuf {
 
 /// Implementation of `io::Reader` trait for a buffered `net::tcp::TcpSocket`
 impl io::Writer for TcpSocketBuf {
-    pub fn write(&self, data: &const [u8]) {
+    pub fn write(&self, data: &[u8]) {
         unsafe {
             let socket_data_ptr: *TcpSocketData =
                 &(*((*(self.data)).sock).socket_data);
diff --git a/src/libstd/serialize.rs b/src/libstd/serialize.rs
index 032df4c819c..29d108e3ac2 100644
--- a/src/libstd/serialize.rs
+++ b/src/libstd/serialize.rs
@@ -20,9 +20,6 @@ use core::hashmap::{HashMap, HashSet};
 use core::trie::{TrieMap, TrieSet};
 use deque::Deque;
 use dlist::DList;
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 use treemap::{TreeMap, TreeSet};
 
 pub trait Encoder {
@@ -730,9 +727,6 @@ impl<D: Decoder> Decodable<D> for TrieSet {
     }
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 impl<
     E: Encoder,
     K: Encodable<E> + Eq + TotalOrd,
@@ -750,9 +744,6 @@ impl<
     }
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 impl<
     D: Decoder,
     K: Decodable<D> + Eq + TotalOrd,
@@ -771,9 +762,6 @@ impl<
     }
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 impl<
     S: Encoder,
     T: Encodable<S> + Eq + TotalOrd
@@ -789,9 +777,6 @@ impl<
     }
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 impl<
     D: Decoder,
     T: Decodable<D> + Eq + TotalOrd
diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs
index 3e6011e80df..c153d7f22c0 100644
--- a/src/libstd/sort.rs
+++ b/src/libstd/sort.rs
@@ -61,6 +61,7 @@ pub fn merge_sort<T:Copy>(v: &const [T], le: Le<T>) -> ~[T] {
     }
 }
 
+#[cfg(stage0)]
 fn part<T>(arr: &mut [T], left: uint,
            right: uint, pivot: uint, compare_func: Le<T>) -> uint {
     arr[pivot] <-> arr[right];
@@ -79,6 +80,23 @@ fn part<T>(arr: &mut [T], left: uint,
     return storage_index;
 }
 
+#[cfg(not(stage0))]
+fn part<T>(arr: &mut [T], left: uint,
+           right: uint, pivot: uint, compare_func: Le<T>) -> uint {
+    arr[pivot] <-> arr[right];
+    let mut storage_index: uint = left;
+    let mut i: uint = left;
+    while i < right {
+        if compare_func(&arr[i], &arr[right]) {
+            arr[i] <-> arr[storage_index];
+            storage_index += 1;
+        }
+        i += 1;
+    }
+    arr[storage_index] <-> arr[right];
+    return storage_index;
+}
+
 fn qsort<T>(arr: &mut [T], left: uint,
             right: uint, compare_func: Le<T>) {
     if right > left {
@@ -162,7 +180,8 @@ fn qsort3<T:Copy + Ord + Eq>(arr: &mut [T], left: int, right: int) {
  */
 pub fn quick_sort3<T:Copy + Ord + Eq>(arr: &mut [T]) {
     if arr.len() <= 1 { return; }
-    qsort3(arr, 0, (arr.len() - 1) as int);
+    let len = arr.len() - 1; // FIXME(#5074) nested calls
+    qsort3(arr, 0, (len - 1) as int);
 }
 
 pub trait Sort {
@@ -195,15 +214,20 @@ pub fn tim_sort<T:Copy + Ord>(array: &mut [T]) {
     let mut idx = 0;
     let mut remaining = size;
     loop {
-        let arr = vec::mut_slice(array, idx, size);
-        let mut run_len: uint = count_run_ascending(arr);
-
-        if run_len < min_run {
-            let force = if remaining <= min_run {remaining} else {min_run};
-            let slice = vec::mut_slice(arr, 0, force);
-            binarysort(slice, run_len);
-            run_len = force;
-        }
+        let run_len: uint = {
+            // This scope contains the slice `arr` here:
+            let arr = vec::mut_slice(array, idx, size);
+            let mut run_len: uint = count_run_ascending(arr);
+
+            if run_len < min_run {
+                let force = if remaining <= min_run {remaining} else {min_run};
+                let slice = vec::mut_slice(arr, 0, force);
+                binarysort(slice, run_len);
+                run_len = force;
+            }
+
+            run_len
+        };
 
         ms.push_run(idx, run_len);
         ms.merge_collapse(array);
@@ -250,7 +274,7 @@ fn binarysort<T:Copy + Ord>(array: &mut [T], start: uint) {
 fn reverse_slice<T>(v: &mut [T], start: uint, end:uint) {
     let mut i = start;
     while i < end / 2 {
-        util::swap(&mut v[i], &mut v[end - i - 1]);
+        v[i] <-> v[end - i - 1];
         i += 1;
     }
 }
@@ -433,14 +457,17 @@ impl<T:Copy + Ord> MergeState<T> {
             self.runs[n+1].len = self.runs[n+2].len;
         }
 
-        let slice = vec::mut_slice(array, b1, b1+l1);
-        let k = gallop_right(&const array[b2], slice, 0);
+        let k = { // constrain lifetime of slice below
+            let slice = vec::mut_slice(array, b1, b1+l1);
+            gallop_right(&const array[b2], slice, 0)
+        };
         b1 += k;
         l1 -= k;
         if l1 != 0 {
-            let slice = vec::mut_slice(array, b2, b2+l2);
-            let l2 = gallop_left(
-                &const array[b1+l1-1],slice,l2-1);
+            let l2 = { // constrain lifetime of slice below
+                let slice = vec::mut_slice(array, b2, b2+l2);
+                gallop_left(&const array[b1+l1-1],slice,l2-1)
+            };
             if l2 > 0 {
                 if l1 <= l2 {
                     self.merge_lo(array, b1, l1, b2, l2);
@@ -621,9 +648,11 @@ impl<T:Copy + Ord> MergeState<T> {
             loop {
                 assert!(len2 > 1 && len1 != 0);
 
-                let tmp_view = vec::mut_slice(array, base1, base1+len1);
-                count1 = len1 - gallop_right(
-                    &const tmp[c2], tmp_view, len1-1);
+                { // constrain scope of tmp_view:
+                    let tmp_view = vec::mut_slice (array, base1, base1+len1);
+                    count1 = len1 - gallop_right(
+                        &const tmp[c2], tmp_view, len1-1);
+                }
 
                 if count1 != 0 {
                     dest -= count1; c1 -= count1; len1 -= count1;
@@ -636,12 +665,11 @@ impl<T:Copy + Ord> MergeState<T> {
                 if len2 == 1 { break_outer = true; break; }
 
                 let count2;
-                {
+                { // constrain scope of tmp_view
                     let tmp_view = vec::mut_slice(tmp, 0, len2);
                     count2 = len2 - gallop_left(&const array[c1],
                                                 tmp_view,
                                                 len2-1);
-                    // Make tmp_view go out of scope to appease borrowck.
                 }
 
                 if count2 != 0 {
diff --git a/src/libstd/std.rc b/src/libstd/std.rc
index 40db9f89d0f..d9af8b111bb 100644
--- a/src/libstd/std.rc
+++ b/src/libstd/std.rc
@@ -71,7 +71,6 @@ pub mod rope;
 pub mod smallintmap;
 pub mod sort;
 pub mod dlist;
-#[cfg(not(stage0))]
 pub mod treemap;
 
 // And ... other stuff
diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs
index f9828ad2b9e..eb131b17c2f 100644
--- a/src/libsyntax/ast_map.rs
+++ b/src/libsyntax/ast_map.rs
@@ -19,6 +19,7 @@ use diagnostic::span_handler;
 use parse::token::ident_interner;
 use print::pprust;
 use visit;
+use syntax::parse::token::special_idents;
 
 use core::hashmap::HashMap;
 
@@ -89,14 +90,13 @@ pub enum ast_node {
     node_variant(variant, @item, @path),
     node_expr(@expr),
     node_stmt(@stmt),
-    // Locals are numbered, because the alias analysis needs to know in which
-    // order they are introduced.
-    node_arg(arg, uint),
-    node_local(uint),
+    node_arg,
+    node_local(ident),
     // Destructor for a struct
     node_dtor(Generics, @struct_dtor, def_id, @path),
     node_block(blk),
     node_struct_ctor(@struct_def, @item, @path),
+    node_callee_scope(@expr)
 }
 
 pub type map = @mut HashMap<node_id, ast_node>;
@@ -104,7 +104,6 @@ pub type map = @mut HashMap<node_id, ast_node>;
 pub struct Ctx {
     map: map,
     path: path,
-    local_id: uint,
     diag: @span_handler,
 }
 
@@ -120,9 +119,8 @@ pub fn mk_ast_map_visitor() -> vt {
         visit_expr: map_expr,
         visit_stmt: map_stmt,
         visit_fn: map_fn,
-        visit_local: map_local,
-        visit_arm: map_arm,
         visit_block: map_block,
+        visit_pat: map_pat,
         .. *visit::default_visitor()
     });
 }
@@ -131,7 +129,6 @@ pub fn map_crate(diag: @span_handler, c: @crate) -> map {
     let cx = @mut Ctx {
         map: @mut HashMap::new(),
         path: ~[],
-        local_id: 0u,
         diag: diag,
     };
     visit::visit_crate(c, cx, mk_ast_map_visitor());
@@ -154,7 +151,6 @@ pub fn map_decoded_item(diag: @span_handler,
     let cx = @mut Ctx {
         map: map,
         path: copy path,
-        local_id: 0,
         diag: diag,
     };
     let v = mk_ast_map_visitor();
@@ -189,9 +185,7 @@ pub fn map_fn(
     v: visit::vt<@mut Ctx>
 ) {
     for decl.inputs.each |a| {
-        cx.map.insert(a.id,
-                      node_arg(/* FIXME (#2543) */ copy *a, cx.local_id));
-        cx.local_id += 1u;
+        cx.map.insert(a.id, node_arg);
     }
     match *fk {
         visit::fk_dtor(generics, ref attrs, self_id, parent_id) => {
@@ -222,33 +216,22 @@ pub fn map_block(b: &blk, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
     visit::visit_block(b, cx, v);
 }
 
-pub fn number_pat(cx: @mut Ctx, pat: @pat) {
-    do ast_util::walk_pat(pat) |p| {
-        match p.node {
-          pat_ident(*) => {
-            cx.map.insert(p.id, node_local(cx.local_id));
-            cx.local_id += 1u;
-          }
-          _ => ()
+pub fn map_pat(pat: @pat, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
+    match pat.node {
+        pat_ident(_, path, _) => {
+            // Note: this is at least *potentially* a pattern...
+            cx.map.insert(pat.id, node_local(ast_util::path_to_ident(path)));
         }
-    };
-}
-
-pub fn map_local(loc: @local, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
-    number_pat(cx, loc.node.pat);
-    visit::visit_local(loc, cx, v);
-}
+        _ => ()
+    }
 
-pub fn map_arm(arm: &arm, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
-    number_pat(cx, arm.pats[0]);
-    visit::visit_arm(arm, cx, v);
+    visit::visit_pat(pat, cx, v);
 }
 
 pub fn map_method(impl_did: def_id, impl_path: @path,
                   m: @method, cx: @mut Ctx) {
     cx.map.insert(m.id, node_method(m, impl_did, impl_path));
-    cx.map.insert(m.self_id, node_local(cx.local_id));
-    cx.local_id += 1u;
+    cx.map.insert(m.self_id, node_local(special_idents::self_));
 }
 
 pub fn map_item(i: @item, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
@@ -317,6 +300,7 @@ pub fn map_item(i: @item, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
         }
         _ => ()
     }
+
     match i.node {
         item_mod(_) | item_foreign_mod(_) => {
             cx.path.push(path_mod(i.ident));
@@ -352,6 +336,18 @@ pub fn map_struct_def(
 
 pub fn map_expr(ex: @expr, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
     cx.map.insert(ex.id, node_expr(ex));
+    match ex.node {
+        // Expressions which are or might be calls:
+        ast::expr_call(*) |
+        ast::expr_method_call(*) |
+        ast::expr_index(*) |
+        ast::expr_binary(*) |
+        ast::expr_assign_op(*) |
+        ast::expr_unary(*) => {
+            cx.map.insert(ex.callee_id, node_callee_scope(ex));
+        }
+        _ => {}
+    }
     visit::visit_expr(ex, cx, v);
 }
 
@@ -401,15 +397,18 @@ pub fn node_id_to_str(map: map, id: node_id, itr: @ident_interner) -> ~str {
       Some(&node_expr(expr)) => {
         fmt!("expr %s (id=%?)", pprust::expr_to_str(expr, itr), id)
       }
+      Some(&node_callee_scope(expr)) => {
+        fmt!("callee_scope %s (id=%?)", pprust::expr_to_str(expr, itr), id)
+      }
       Some(&node_stmt(stmt)) => {
         fmt!("stmt %s (id=%?)",
              pprust::stmt_to_str(stmt, itr), id)
       }
-      Some(&node_arg(_, _)) => { // add more info here
+      Some(&node_arg) => {
         fmt!("arg (id=%?)", id)
       }
-      Some(&node_local(_)) => { // add more info here
-        fmt!("local (id=%?)", id)
+      Some(&node_local(ident)) => {
+        fmt!("local (id=%?, name=%s)", id, *itr.get(ident))
       }
       Some(&node_dtor(*)) => { // add more info here
         fmt!("node_dtor (id=%?)", id)
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 148b713a4f5..7e24adabdb0 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -388,8 +388,20 @@ pub struct id_range {
     max: node_id,
 }
 
-pub fn empty(range: id_range) -> bool {
-    range.min >= range.max
+pub impl id_range {
+    fn max() -> id_range {
+        id_range {min: int::max_value,
+                  max: int::min_value}
+    }
+
+    fn empty(&self) -> bool {
+        self.min >= self.max
+    }
+
+    fn add(&mut self, id: node_id) {
+        self.min = int::min(self.min, id);
+        self.max = int::max(self.max, id + 1);
+    }
 }
 
 pub fn id_visitor(vfn: @fn(node_id)) -> visit::vt<()> {
@@ -493,13 +505,11 @@ pub fn visit_ids_for_inlined_item(item: &inlined_item, vfn: @fn(node_id)) {
 }
 
 pub fn compute_id_range(visit_ids_fn: &fn(@fn(node_id))) -> id_range {
-    let min = @mut int::max_value;
-    let max = @mut int::min_value;
+    let result = @mut id_range::max();
     do visit_ids_fn |id| {
-        *min = int::min(*min, id);
-        *max = int::max(*max, id + 1);
+        result.add(id);
     }
-    id_range { min: *min, max: *max }
+    *result
 }
 
 pub fn compute_id_range_for_inlined_item(item: &inlined_item) -> id_range {
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 1194506a887..7facc181eff 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -246,7 +246,7 @@ pub impl FileMap {
         // the new charpos must be > the last one (or it's the first one).
         let lines = &mut *self.lines;
         assert!((lines.len() == 0) || (lines[lines.len() - 1] < pos));
-        self.lines.push(pos);
+        lines.push(pos);
     }
 
     // get a line from the list of pre-computed line-beginnings
@@ -308,7 +308,7 @@ pub impl CodeMap {
             multibyte_chars: @mut ~[],
         };
 
-        self.files.push(filemap);
+        files.push(filemap);
 
         return filemap;
     }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 5bad9ecae3e..7d058f22e4c 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -210,29 +210,29 @@ pub fn syntax_expander_table() -> SyntaxEnv {
 // when a macro expansion occurs, the resulting nodes have the backtrace()
 // -> expn_info of their expansion context stored into their span.
 pub trait ext_ctxt {
-    fn codemap(@mut self) -> @CodeMap;
-    fn parse_sess(@mut self) -> @mut parse::ParseSess;
-    fn cfg(@mut self) -> ast::crate_cfg;
-    fn call_site(@mut self) -> span;
-    fn print_backtrace(@mut self);
-    fn backtrace(@mut self) -> Option<@ExpnInfo>;
-    fn mod_push(@mut self, mod_name: ast::ident);
-    fn mod_pop(@mut self);
-    fn mod_path(@mut self) -> ~[ast::ident];
-    fn bt_push(@mut self, ei: codemap::ExpnInfo);
-    fn bt_pop(@mut self);
-    fn span_fatal(@mut self, sp: span, msg: &str) -> !;
-    fn span_err(@mut self, sp: span, msg: &str);
-    fn span_warn(@mut self, sp: span, msg: &str);
-    fn span_unimpl(@mut self, sp: span, msg: &str) -> !;
-    fn span_bug(@mut self, sp: span, msg: &str) -> !;
-    fn bug(@mut self, msg: &str) -> !;
-    fn next_id(@mut self) -> ast::node_id;
-    fn trace_macros(@mut self) -> bool;
-    fn set_trace_macros(@mut self, x: bool);
+    fn codemap(&self) -> @CodeMap;
+    fn parse_sess(&self) -> @mut parse::ParseSess;
+    fn cfg(&self) -> ast::crate_cfg;
+    fn call_site(&self) -> span;
+    fn print_backtrace(&self);
+    fn backtrace(&self) -> Option<@ExpnInfo>;
+    fn mod_push(&self, mod_name: ast::ident);
+    fn mod_pop(&self);
+    fn mod_path(&self) -> ~[ast::ident];
+    fn bt_push(&self, ei: codemap::ExpnInfo);
+    fn bt_pop(&self);
+    fn span_fatal(&self, sp: span, msg: &str) -> !;
+    fn span_err(&self, sp: span, msg: &str);
+    fn span_warn(&self, sp: span, msg: &str);
+    fn span_unimpl(&self, sp: span, msg: &str) -> !;
+    fn span_bug(&self, sp: span, msg: &str) -> !;
+    fn bug(&self, msg: &str) -> !;
+    fn next_id(&self) -> ast::node_id;
+    fn trace_macros(&self) -> bool;
+    fn set_trace_macros(&self, x: bool);
     /* for unhygienic identifier transformation */
-    fn str_of(@mut self, id: ast::ident) -> ~str;
-    fn ident_of(@mut self, st: ~str) -> ast::ident;
+    fn str_of(&self, id: ast::ident) -> ~str;
+    fn ident_of(&self, st: ~str) -> ast::ident;
 }
 
 pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
@@ -241,25 +241,31 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
         parse_sess: @mut parse::ParseSess,
         cfg: ast::crate_cfg,
         backtrace: @mut Option<@ExpnInfo>,
-        mod_path: ~[ast::ident],
-        trace_mac: bool
+
+        // These two @mut's should really not be here,
+        // but the self types for CtxtRepr are all wrong
+        // and there are bugs in the code for object
+        // types that make this hard to get right at the
+        // moment. - nmatsakis
+        mod_path: @mut ~[ast::ident],
+        trace_mac: @mut bool
     }
     impl ext_ctxt for CtxtRepr {
-        fn codemap(@mut self) -> @CodeMap { self.parse_sess.cm }
-        fn parse_sess(@mut self) -> @mut parse::ParseSess { self.parse_sess }
-        fn cfg(@mut self) -> ast::crate_cfg { copy self.cfg }
-        fn call_site(@mut self) -> span {
+        fn codemap(&self) -> @CodeMap { self.parse_sess.cm }
+        fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess }
+        fn cfg(&self) -> ast::crate_cfg { copy self.cfg }
+        fn call_site(&self) -> span {
             match *self.backtrace {
                 Some(@ExpandedFrom(CallInfo {call_site: cs, _})) => cs,
                 None => self.bug(~"missing top span")
             }
         }
-        fn print_backtrace(@mut self) { }
-        fn backtrace(@mut self) -> Option<@ExpnInfo> { *self.backtrace }
-        fn mod_push(@mut self, i: ast::ident) { self.mod_path.push(i); }
-        fn mod_pop(@mut self) { self.mod_path.pop(); }
-        fn mod_path(@mut self) -> ~[ast::ident] { copy self.mod_path }
-        fn bt_push(@mut self, ei: codemap::ExpnInfo) {
+        fn print_backtrace(&self) { }
+        fn backtrace(&self) -> Option<@ExpnInfo> { *self.backtrace }
+        fn mod_push(&self, i: ast::ident) { self.mod_path.push(i); }
+        fn mod_pop(&self) { self.mod_path.pop(); }
+        fn mod_path(&self) -> ~[ast::ident] { copy *self.mod_path }
+        fn bt_push(&self, ei: codemap::ExpnInfo) {
             match ei {
               ExpandedFrom(CallInfo {call_site: cs, callee: ref callee}) => {
                 *self.backtrace =
@@ -270,7 +276,7 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
               }
             }
         }
-        fn bt_pop(@mut self) {
+        fn bt_pop(&self) {
             match *self.backtrace {
               Some(@ExpandedFrom(CallInfo {
                   call_site: span {expn_info: prev, _}, _
@@ -280,52 +286,52 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
               _ => self.bug(~"tried to pop without a push")
             }
         }
-        fn span_fatal(@mut self, sp: span, msg: &str) -> ! {
+        fn span_fatal(&self, sp: span, msg: &str) -> ! {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_fatal(sp, msg);
         }
-        fn span_err(@mut self, sp: span, msg: &str) {
+        fn span_err(&self, sp: span, msg: &str) {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_err(sp, msg);
         }
-        fn span_warn(@mut self, sp: span, msg: &str) {
+        fn span_warn(&self, sp: span, msg: &str) {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_warn(sp, msg);
         }
-        fn span_unimpl(@mut self, sp: span, msg: &str) -> ! {
+        fn span_unimpl(&self, sp: span, msg: &str) -> ! {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
         }
-        fn span_bug(@mut self, sp: span, msg: &str) -> ! {
+        fn span_bug(&self, sp: span, msg: &str) -> ! {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_bug(sp, msg);
         }
-        fn bug(@mut self, msg: &str) -> ! {
+        fn bug(&self, msg: &str) -> ! {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.handler().bug(msg);
         }
-        fn next_id(@mut self) -> ast::node_id {
+        fn next_id(&self) -> ast::node_id {
             return parse::next_node_id(self.parse_sess);
         }
-        fn trace_macros(@mut self) -> bool {
-            self.trace_mac
+        fn trace_macros(&self) -> bool {
+            *self.trace_mac
         }
-        fn set_trace_macros(@mut self, x: bool) {
-            self.trace_mac = x
+        fn set_trace_macros(&self, x: bool) {
+            *self.trace_mac = x
         }
-        fn str_of(@mut self, id: ast::ident) -> ~str {
+        fn str_of(&self, id: ast::ident) -> ~str {
             copy *self.parse_sess.interner.get(id)
         }
-        fn ident_of(@mut self, st: ~str) -> ast::ident {
+        fn ident_of(&self, st: ~str) -> ast::ident {
             self.parse_sess.interner.intern(@/*bad*/ copy st)
         }
     }
-    let imp: @mut CtxtRepr = @mut CtxtRepr {
+    let imp: @CtxtRepr = @CtxtRepr {
         parse_sess: parse_sess,
         cfg: cfg,
         backtrace: @mut None,
-        mod_path: ~[],
-        trace_mac: false
+        mod_path: @mut ~[],
+        trace_mac: @mut false
     };
     ((imp) as @ext_ctxt)
 }
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index fde5a259422..841f64e0b05 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -27,6 +27,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
                    fld: @ast_fold,
                    orig: @fn(&expr_, span, @ast_fold) -> (expr_, span))
                 -> (expr_, span) {
+    let mut cx = cx;
     match *e {
         // expr_mac should really be expr_ext or something; it's the
         // entry-point for all syntax extensions.
@@ -112,6 +113,8 @@ pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
                         fld: @ast_fold,
                         orig: @fn(&ast::_mod, @ast_fold) -> ast::_mod)
                      -> ast::_mod {
+    let mut cx = cx;
+
     // Fold the contents first:
     let module_ = orig(module_, fld);
 
diff --git a/src/libsyntax/ext/pipes/liveness.rs b/src/libsyntax/ext/pipes/liveness.rs
index 4597dab89cb..7843db55789 100644
--- a/src/libsyntax/ext/pipes/liveness.rs
+++ b/src/libsyntax/ext/pipes/liveness.rs
@@ -38,11 +38,11 @@ updating the states using rule (2) until there are no changes.
 */
 
 use ext::base::ext_ctxt;
-use ext::pipes::proto::protocol;
+use ext::pipes::proto::{protocol_};
 
 use std::bitv::Bitv;
 
-pub fn analyze(proto: protocol, _cx: @ext_ctxt) {
+pub fn analyze(proto: &mut protocol_, _cx: @ext_ctxt) {
     debug!("initializing colive analysis");
     let num_states = proto.num_states();
     let mut colive = do (copy proto.states).map_to_vec |state| {
diff --git a/src/libsyntax/ext/pipes/proto.rs b/src/libsyntax/ext/pipes/proto.rs
index 79072a2f577..ffb55ee50d9 100644
--- a/src/libsyntax/ext/pipes/proto.rs
+++ b/src/libsyntax/ext/pipes/proto.rs
@@ -138,26 +138,26 @@ pub struct protocol_ {
 
 pub impl protocol_ {
     /// Get a state.
-    fn get_state(&mut self, name: ~str) -> state {
+    fn get_state(&self, name: ~str) -> state {
         self.states.find(|i| i.name == name).get()
     }
 
-    fn get_state_by_id(&mut self, id: uint) -> state { self.states[id] }
+    fn get_state_by_id(&self, id: uint) -> state { self.states[id] }
 
-    fn has_state(&mut self, name: ~str) -> bool {
+    fn has_state(&self, name: ~str) -> bool {
         self.states.find(|i| i.name == name).is_some()
     }
 
-    fn filename(&mut self) -> ~str {
+    fn filename(&self) -> ~str {
         ~"proto://" + self.name
     }
 
-    fn num_states(&mut self) -> uint {
+    fn num_states(&self) -> uint {
         let states = &mut *self.states;
         states.len()
     }
 
-    fn has_ty_params(&mut self) -> bool {
+    fn has_ty_params(&self) -> bool {
         for self.states.each |s| {
             if s.generics.ty_params.len() > 0 {
                 return true;
@@ -165,7 +165,7 @@ pub impl protocol_ {
         }
         false
     }
-    fn is_bounded(&mut self) -> bool {
+    fn is_bounded(&self) -> bool {
         let bounded = self.bounded.get();
         bounded
     }
@@ -179,7 +179,7 @@ pub impl protocol_ {
                       generics: ast::Generics)
                    -> state {
         let messages = @mut ~[];
-        let states = &*self.states;
+        let states = &mut *self.states;
 
         let state = @state_ {
             id: states.len(),
@@ -192,7 +192,7 @@ pub impl protocol_ {
             proto: self
         };
 
-        self.states.push(state);
+        states.push(state);
         state
     }
 }
diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs
index e2ad5becb12..c1acee8e2cd 100644
--- a/src/libsyntax/print/pp.rs
+++ b/src/libsyntax/print/pp.rs
@@ -491,9 +491,9 @@ pub impl Printer {
           }
           END => {
             debug!("print END -> pop END");
-            let print_stack = &*self.print_stack;
+            let print_stack = &mut *self.print_stack;
             assert!((print_stack.len() != 0u));
-            self.print_stack.pop();
+            print_stack.pop();
           }
           BREAK(b) => {
             let top = self.get_top();
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index d5645ada929..ff8259e8996 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -72,6 +72,12 @@ pub fn end(s: @ps) {
 }
 
 pub fn rust_printer(writer: @io::Writer, intr: @ident_interner) -> @ps {
+    return rust_printer_annotated(writer, intr, no_ann());
+}
+
+pub fn rust_printer_annotated(writer: @io::Writer,
+                              intr: @ident_interner,
+                              ann: pp_ann) -> @ps {
     return @ps {
         s: pp::mk_printer(writer, default_columns),
         cm: None::<@CodeMap>,
@@ -83,7 +89,7 @@ pub fn rust_printer(writer: @io::Writer, intr: @ident_interner) -> @ps {
             cur_lit: 0
         },
         boxes: @mut ~[],
-        ann: no_ann()
+        ann: ann
     };
 }
 
diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs
index 9ab7d4bc443..e3a87277622 100644
--- a/src/libsyntax/util/interner.rs
+++ b/src/libsyntax/util/interner.rs
@@ -44,10 +44,10 @@ pub impl<T:Eq + IterBytes + Hash + Const + Copy> Interner<T> {
             None => (),
         }
 
-        let vect = &*self.vect;
+        let vect = &mut *self.vect;
         let new_idx = vect.len();
         self.map.insert(val, new_idx);
-        self.vect.push(val);
+        vect.push(val);
         new_idx
     }
 
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 80df8fb91a5..a42f640a175 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -22,6 +22,12 @@ use opt_vec::OptVec;
 // children (potentially passing in different contexts to each), call
 // visit::visit_* to apply the default traversal algorithm (again, it can
 // override the context), or prevent deeper traversal by doing nothing.
+//
+// Note: it is an important invariant that the default visitor walks the body
+// of a function in "execution order" (more concretely, reverse post-order
+// with respect to the CFG implied by the AST), meaning that if AST node A may
+// execute before AST node B, then A is visited first.  The borrow checker in
+// particular relies on this property.
 
 // Our typesystem doesn't do circular types, so the visitor record can not
 // hold functions that take visitors. A vt enum is used to break the cycle.
diff --git a/src/test/compile-fail/access-mode-in-closures.rs b/src/test/compile-fail/access-mode-in-closures.rs
index f6b9a82ec67..61fb754f761 100644
--- a/src/test/compile-fail/access-mode-in-closures.rs
+++ b/src/test/compile-fail/access-mode-in-closures.rs
@@ -16,6 +16,6 @@ fn unpack(_unpack: &fn(v: &sty) -> ~[int]) {}
 fn main() {
     let _foo = unpack(|s| {
         // Test that `s` is moved here.
-        match *s { sty(v) => v } //~ ERROR moving out of dereference of immutable & pointer
+        match *s { sty(v) => v } //~ ERROR cannot move out
     });
 }
diff --git a/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs
index e2dd13a4405..85f60f34bdb 100644
--- a/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs
+++ b/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs
@@ -17,6 +17,7 @@ fn main() {
         y = Some(x.downgrade(write_mode));
         //~^ ERROR cannot infer an appropriate lifetime
     }
+    y.get();
     // Adding this line causes a method unification failure instead
     // do (&option::unwrap(y)).read |state| { assert!(*state == 1); }
 }
diff --git a/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs
index 78a50a4f212..c7ae6a0dc6c 100644
--- a/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs
+++ b/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs
@@ -17,6 +17,7 @@ fn main() {
     do x.write_downgrade |write_mode| {
         y = Some(write_mode);
     }
+    y.get();
     // Adding this line causes a method unification failure instead
     // do (&option::unwrap(y)).write |state| { assert!(*state == 1); }
 }
diff --git a/src/test/compile-fail/attempted-access-non-fatal.rs b/src/test/compile-fail/attempted-access-non-fatal.rs
index ba15abc3f89..1d9249bc17b 100644
--- a/src/test/compile-fail/attempted-access-non-fatal.rs
+++ b/src/test/compile-fail/attempted-access-non-fatal.rs
@@ -11,6 +11,6 @@
 // Check that bogus field access is non-fatal
 fn main() {
     let x = 0;
-    debug!(x.foo); //~ ERROR attempted access of field
-    debug!(x.bar); //~ ERROR attempted access of field
+    let _ = x.foo; //~ ERROR attempted access of field
+    let _ = x.bar; //~ ERROR attempted access of field
 }
diff --git a/src/test/compile-fail/borrowck-addr-of-upvar.rs b/src/test/compile-fail/borrowck-addr-of-upvar.rs
index 640bc887731..83baedc7892 100644
--- a/src/test/compile-fail/borrowck-addr-of-upvar.rs
+++ b/src/test/compile-fail/borrowck-addr-of-upvar.rs
@@ -9,12 +9,12 @@
 // except according to those terms.
 
 fn foo(x: @int) -> @fn() -> &'static int {
-    let result: @fn() -> &'static int = || &*x;  //~ ERROR illegal borrow
+    let result: @fn() -> &'static int = || &*x;  //~ ERROR cannot root
     result
 }
 
 fn bar(x: @int) -> @fn() -> &int {
-    let result: @fn() -> &int = || &*x; //~ ERROR illegal borrow
+    let result: @fn() -> &int = || &*x; //~ ERROR cannot root
     result
 }
 
diff --git a/src/test/compile-fail/borrowck-assign-comp-idx.rs b/src/test/compile-fail/borrowck-assign-comp-idx.rs
index 25b56abb5ba..d447d9e4a22 100644
--- a/src/test/compile-fail/borrowck-assign-comp-idx.rs
+++ b/src/test/compile-fail/borrowck-assign-comp-idx.rs
@@ -17,9 +17,11 @@ fn a() {
     let mut p = ~[1];
 
     // Create an immutable pointer into p's contents:
-    let _q: &int = &p[0]; //~ NOTE loan of mutable vec content granted here
+    let q: &int = &p[0];
 
-    p[0] = 5; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan
+    p[0] = 5; //~ ERROR cannot assign
+
+    debug!("%d", *q);
 }
 
 fn borrow(_x: &[int], _f: &fn()) {}
@@ -30,8 +32,8 @@ fn b() {
 
     let mut p = ~[1];
 
-    do borrow(p) { //~ NOTE loan of mutable vec content granted here
-        p[0] = 5; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan
+    do borrow(p) {
+        p[0] = 5; //~ ERROR cannot assign to
     }
 }
 
diff --git a/src/test/compile-fail/borrowck-assign-comp.rs b/src/test/compile-fail/borrowck-assign-comp.rs
index 283f04a283f..b8c0cbe9743 100644
--- a/src/test/compile-fail/borrowck-assign-comp.rs
+++ b/src/test/compile-fail/borrowck-assign-comp.rs
@@ -12,12 +12,13 @@ struct point { x: int, y: int }
 
 fn a() {
     let mut p = point {x: 3, y: 4};
-    let _q = &p; //~ NOTE loan of mutable local variable granted here
+    let q = &p;
 
     // This assignment is illegal because the field x is not
     // inherently mutable; since `p` was made immutable, `p.x` is now
     // immutable.  Otherwise the type of &_q.x (&int) would be wrong.
-    p.x = 5; //~ ERROR assigning to mutable field prohibited due to outstanding loan
+    p.x = 5; //~ ERROR cannot assign to `p.x`
+    q.x;
 }
 
 fn c() {
@@ -25,9 +26,10 @@ fn c() {
     // and then try to overwrite `p` as a whole.
 
     let mut p = point {x: 3, y: 4};
-    let _q = &p.y; //~ NOTE loan of mutable local variable granted here
-    p = point {x: 5, y: 7};//~ ERROR assigning to mutable local variable prohibited due to outstanding loan
-    copy p;
+    let q = &p.y;
+    p = point {x: 5, y: 7};//~ ERROR cannot assign to `p`
+    p.x; // silence warning
+    *q; // stretch loan
 }
 
 fn d() {
@@ -35,9 +37,9 @@ fn d() {
     // address of a subcomponent and then modify that subcomponent:
 
     let mut p = point {x: 3, y: 4};
-    let _q = &p.y; //~ NOTE loan of mutable field granted here
-    p.y = 5; //~ ERROR assigning to mutable field prohibited due to outstanding loan
-    copy p;
+    let q = &p.y;
+    p.y = 5; //~ ERROR cannot assign to `p.y`
+    *q;
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-assign-to-constants.rs b/src/test/compile-fail/borrowck-assign-to-constants.rs
index 0d65aacb65b..f0dc28b736d 100644
--- a/src/test/compile-fail/borrowck-assign-to-constants.rs
+++ b/src/test/compile-fail/borrowck-assign-to-constants.rs
@@ -12,6 +12,6 @@ static foo: int = 5;
 
 fn main() {
     // assigning to various global constants
-    None = Some(3); //~ ERROR assigning to static item
-    foo = 6; //~ ERROR assigning to static item
+    None = Some(3); //~ ERROR cannot assign to immutable static item
+    foo = 6; //~ ERROR cannot assign to immutable static item
 }
diff --git a/src/test/compile-fail/borrowck-assign-to-enum.rs b/src/test/compile-fail/borrowck-assign-to-enum.rs
index a35d88a76f3..fcaba0adc46 100644
--- a/src/test/compile-fail/borrowck-assign-to-enum.rs
+++ b/src/test/compile-fail/borrowck-assign-to-enum.rs
@@ -12,5 +12,5 @@ struct foo(int);
 
 fn main() {
     let x = foo(3);
-    *x = 4; //~ ERROR assigning to anonymous field
+    *x = 4; //~ ERROR cannot assign to immutable anonymous field
 }
diff --git a/src/test/compile-fail/borrowck-assign-to-subfield.rs b/src/test/compile-fail/borrowck-assign-to-subfield.rs
index 610802ca68b..2ee5ecfcb9c 100644
--- a/src/test/compile-fail/borrowck-assign-to-subfield.rs
+++ b/src/test/compile-fail/borrowck-assign-to-subfield.rs
@@ -34,6 +34,6 @@ fn main() {
 
     // in these cases we pass through a box, so the mut
     // of the box is dominant
-    p.x.a = 2;     //~ ERROR assigning to immutable field
+    p.x.a = 2;     //~ ERROR cannot assign to immutable field
     p.z.a = 2;
 }
diff --git a/src/test/compile-fail/auto-ref-borrowck-failure.rs b/src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs
index 90b9b44cfbe..ce95ee94f42 100644
--- a/src/test/compile-fail/auto-ref-borrowck-failure.rs
+++ b/src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs
@@ -14,18 +14,14 @@ struct Foo {
     x: int
 }
 
-trait Stuff {
-    fn printme(self);
-}
-
-impl<'self> Stuff for &'self mut Foo {
-    fn printme(self) {
+pub impl Foo {
+    fn printme(&mut self) {
         io::println(fmt!("%d", self.x));
     }
 }
 
 fn main() {
     let x = Foo { x: 3 };
-    x.printme();    //~ ERROR illegal borrow
+    x.printme();    //~ ERROR cannot borrow
 }
 
diff --git a/src/test/compile-fail/borrowck-autoref-3261.rs b/src/test/compile-fail/borrowck-autoref-3261.rs
index c95b93445ad..192fe669f57 100644
--- a/src/test/compile-fail/borrowck-autoref-3261.rs
+++ b/src/test/compile-fail/borrowck-autoref-3261.rs
@@ -17,10 +17,10 @@ pub impl X {
 }
 fn main() {
     let mut x = X(Right(main));
-    do (&mut x).with |opt| {  //~ ERROR illegal borrow
+    do (&mut x).with |opt| {
         match opt {
             &Right(ref f) => {
-                x = X(Left((0,0))); //~ ERROR assigning to captured outer mutable variable
+                x = X(Left((0,0))); //~ ERROR cannot assign to `x`
                 (*f)()
             },
             _ => fail!()
diff --git a/src/test/compile-fail/borrowck-bad-nested-calls-free.rs b/src/test/compile-fail/borrowck-bad-nested-calls-free.rs
new file mode 100644
index 00000000000..ff1ec38ad64
--- /dev/null
+++ b/src/test/compile-fail/borrowck-bad-nested-calls-free.rs
@@ -0,0 +1,43 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we detect nested calls that could free pointers evaluated
+// for earlier arguments.
+
+fn rewrite(v: &mut ~uint) -> uint {
+    *v = ~22;
+    **v
+}
+
+fn add(v: &uint, w: uint) -> uint {
+    *v + w
+}
+
+fn implicit() {
+    let mut a = ~1;
+
+    // Note the danger here:
+    //
+    //    the pointer for the first argument has already been
+    //    evaluated, but it gets freed when evaluating the second
+    //    argument!
+    add(
+        a,
+        rewrite(&mut a)); //~ ERROR cannot borrow
+}
+
+fn explicit() {
+    let mut a = ~1;
+    add(
+        &*a,
+        rewrite(&mut a)); //~ ERROR cannot borrow
+}
+
+fn main() {}
\ No newline at end of file
diff --git a/src/test/compile-fail/borrowck-bad-nested-calls-move.rs b/src/test/compile-fail/borrowck-bad-nested-calls-move.rs
new file mode 100644
index 00000000000..0adf486b8b3
--- /dev/null
+++ b/src/test/compile-fail/borrowck-bad-nested-calls-move.rs
@@ -0,0 +1,43 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we detect nested calls that could free pointers evaluated
+// for earlier arguments.
+
+fn rewrite(v: &mut ~uint) -> uint {
+    *v = ~22;
+    **v
+}
+
+fn add(v: &uint, w: ~uint) -> uint {
+    *v + *w
+}
+
+fn implicit() {
+    let mut a = ~1;
+
+    // Note the danger here:
+    //
+    //    the pointer for the first argument has already been
+    //    evaluated, but it gets moved when evaluating the second
+    //    argument!
+    add(
+        a,
+        a); //~ ERROR cannot move
+}
+
+fn explicit() {
+    let mut a = ~1;
+    add(
+        &*a,
+        a); //~ ERROR cannot move
+}
+
+fn main() {}
\ No newline at end of file
diff --git a/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs
index 005908f86d8..1051c5829ec 100644
--- a/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs
+++ b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs
@@ -22,32 +22,37 @@ fn make_foo() -> ~Foo { fail!() }
 
 fn borrow_same_field_twice_mut_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_mut_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
-    let _bar2 = &foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1;
+    let _bar2 = &foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_imm_mut() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_imm_imm() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1;
+    let bar1 = &foo.bar1;
     let _bar2 = &foo.bar1;
+    *bar1;
 }
 
-fn borrow_both_mut() {
+fn borrow_both_fields_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     let _bar2 = &mut foo.bar2;
+    *bar1;
 }
 
 fn borrow_both_mut_pattern() {
@@ -59,66 +64,77 @@ fn borrow_both_mut_pattern() {
 
 fn borrow_var_and_pattern() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     match *foo {
         Foo { bar1: ref mut _bar1, bar2: _ } => {}
-        //~^ ERROR conflicts with prior loan
+        //~^ ERROR cannot borrow
     }
+    *bar1;
 }
 
 fn borrow_mut_and_base_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan
-    let _foo2 = &*foo; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo1 = &foo.bar1; //~ ERROR cannot borrow
+    let _foo2 = &*foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_mut_and_base_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_mut_and_base_mut2() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo2 = &mut *foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_mut() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_mut2() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
-    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1.int1;
+    let _foo2 = &mut *foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_imm() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
+    let bar1 = &foo.bar1.int1;
     let _foo1 = &foo.bar1;
     let _foo2 = &*foo;
+    *bar1;
 }
 
 fn borrow_mut_and_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     let _foo1 = &foo.bar2;
+    *bar1;
 }
 
 fn borrow_mut_from_imm() {
     let foo = make_foo();
-    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+    let bar1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_long_path_both_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &mut foo.bar2.int2;
+    let bar1 = &mut foo.bar1.int1;
+    let foo1 = &mut foo.bar2.int2;
+    *bar1;
+    *foo1;
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs
index 035e293bc36..cdcf50c906e 100644
--- a/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs
+++ b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs
@@ -22,32 +22,37 @@ fn make_foo() -> Foo { fail!() }
 
 fn borrow_same_field_twice_mut_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_mut_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
-    let _bar2 = &foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1;
+    let _bar2 = &foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_imm_mut() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_imm_imm() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1;
+    let bar1 = &foo.bar1;
     let _bar2 = &foo.bar1;
+    *bar1;
 }
 
 fn borrow_both_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     let _bar2 = &mut foo.bar2;
+    *bar1;
 }
 
 fn borrow_both_mut_pattern() {
@@ -59,66 +64,76 @@ fn borrow_both_mut_pattern() {
 
 fn borrow_var_and_pattern() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     match foo {
-        Foo { bar1: ref mut _bar1, bar2: _ } => {}
-        //~^ ERROR conflicts with prior loan
+        Foo { bar1: ref mut _bar1, bar2: _ } => {} //
+        //~^ ERROR cannot borrow
     }
+    *bar1;
 }
 
 fn borrow_mut_and_base_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan
-    let _foo2 = &foo; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo1 = &foo.bar1; //~ ERROR cannot borrow
+    let _foo2 = &foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_mut_and_base_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_mut_and_base_mut2() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo2 = &mut foo; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo2 = &mut foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_mut() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_mut2() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
-    let _foo2 = &mut foo; //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1.int1;
+    let _foo2 = &mut foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_imm() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
+    let bar1 = &foo.bar1.int1;
     let _foo1 = &foo.bar1;
     let _foo2 = &foo;
+    *bar1;
 }
 
 fn borrow_mut_and_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     let _foo1 = &foo.bar2;
+    *bar1;
 }
 
 fn borrow_mut_from_imm() {
     let foo = make_foo();
-    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+    let bar1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_long_path_both_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
+    let bar1 = &mut foo.bar1.int1;
     let _foo1 = &mut foo.bar2.int2;
+    *bar1;
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs
index 4a6a90ae516..1e5c4c5cc41 100644
--- a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs
+++ b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs
@@ -28,5 +28,6 @@ fn defer<'r>(x: &'r [&'r str]) -> defer<'r> {
 }
 
 fn main() {
-    let _x = defer(~["Goodbye", "world!"]); //~ ERROR illegal borrow
+    let x = defer(~["Goodbye", "world!"]); //~ ERROR borrowed value does not live long enough
+    x.x[0];
 }
diff --git a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs
index bda659aa7b9..887cb59930e 100644
--- a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs
+++ b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs
@@ -15,7 +15,7 @@ use core::hashmap::HashMap;
 fn main() {
     let mut buggy_map :HashMap<uint, &uint> =
       HashMap::new::<uint, &uint>();
-    buggy_map.insert(42, &*~1); //~ ERROR illegal borrow
+    buggy_map.insert(42, &*~1); //~ ERROR borrowed value does not live long enough
 
     // but it is ok if we use a temporary
     let tmp = ~2;
diff --git a/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
index 2c68429baec..be51091c1fd 100644
--- a/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
+++ b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
@@ -27,13 +27,15 @@ fn a(x: &mut Foo) {
 fn b(x: &Foo) {
     x.f();
     x.g();
-    x.h(); //~ ERROR illegal borrow
+    x.h(); //~ ERROR cannot borrow
 }
 
 fn c(x: &const Foo) {
-    x.f(); //~ ERROR illegal borrow unless pure
+    x.f(); //~ ERROR cannot borrow
+    //~^ ERROR unsafe borrow
     x.g();
-    x.h(); //~ ERROR illegal borrow
+    x.h(); //~ ERROR cannot borrow
+    //~^ ERROR unsafe borrow
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs b/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs
index 88db5f54341..8af10231921 100644
--- a/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs
+++ b/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs
@@ -10,9 +10,9 @@
 
 fn main() {
     let mut _a = 3;
-    let _b = &mut _a; //~ NOTE loan of mutable local variable granted here
+    let _b = &mut _a;
     {
         let _c = &*_b;
-        _a = 4; //~ ERROR assigning to mutable local variable prohibited
+        _a = 4; //~ ERROR cannot assign to `_a`
     }
 }
diff --git a/src/test/compile-fail/borrowck-insert-during-each.rs b/src/test/compile-fail/borrowck-insert-during-each.rs
index 17c0efe225e..109753b38e7 100644
--- a/src/test/compile-fail/borrowck-insert-during-each.rs
+++ b/src/test/compile-fail/borrowck-insert-during-each.rs
@@ -23,8 +23,8 @@ pub impl Foo {
 }
 
 fn bar(f: &mut Foo) {
-  do f.foo |a| { //~ NOTE prior loan as mutable granted here
-    f.n.insert(*a); //~ ERROR conflicts with prior loan
+  do f.foo |a| {
+    f.n.insert(*a); //~ ERROR cannot borrow
   }
 }
 
diff --git a/src/test/compile-fail/borrowck-issue-2657-1.rs b/src/test/compile-fail/borrowck-issue-2657-1.rs
index ce183c1888f..8bcd5f9a72e 100644
--- a/src/test/compile-fail/borrowck-issue-2657-1.rs
+++ b/src/test/compile-fail/borrowck-issue-2657-1.rs
@@ -10,9 +10,9 @@
 
 fn main() {
 let x = Some(~1);
-match x { //~ NOTE loan of immutable local variable granted here
+match x {
   Some(ref _y) => {
-    let _a = x; //~ ERROR moving out of immutable local variable prohibited due to outstanding loan
+    let _a = x; //~ ERROR cannot move
   }
   _ => {}
 }
diff --git a/src/test/compile-fail/borrowck-issue-2657-2.rs b/src/test/compile-fail/borrowck-issue-2657-2.rs
index d2217778d41..fac805c57ca 100644
--- a/src/test/compile-fail/borrowck-issue-2657-2.rs
+++ b/src/test/compile-fail/borrowck-issue-2657-2.rs
@@ -12,7 +12,7 @@ fn main() {
 let x = Some(~1);
 match x {
   Some(ref y) => {
-    let _b = *y; //~ ERROR moving out of dereference of immutable & pointer
+    let _b = *y; //~ ERROR cannot move out
   }
   _ => {}
 }
diff --git a/src/test/compile-fail/borrowck-lend-flow-if.rs b/src/test/compile-fail/borrowck-lend-flow-if.rs
new file mode 100644
index 00000000000..563f63b98be
--- /dev/null
+++ b/src/test/compile-fail/borrowck-lend-flow-if.rs
@@ -0,0 +1,52 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// 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
+// either genuine or would require more advanced changes.  The latter
+// cases are noted.
+
+fn borrow(_v: &int) {}
+fn borrow_mut(_v: &mut int) {}
+fn cond() -> bool { fail!() }
+fn for_func(_f: &fn() -> bool) { fail!() }
+fn produce<T>() -> T { fail!(); }
+
+fn inc(v: &mut ~int) {
+    *v = ~(**v + 1);
+}
+
+fn pre_freeze_cond() {
+    // In this instance, the freeze is conditional and starts before
+    // the mut borrow.
+
+    let mut v = ~3;
+    let _w;
+    if cond() {
+        _w = &v;
+    }
+    borrow_mut(v); //~ ERROR cannot borrow
+}
+
+fn pre_freeze_else() {
+    // In this instance, the freeze and mut borrow are on separate sides
+    // of the if.
+
+    let mut v = ~3;
+    let _w;
+    if cond() {
+        _w = &v;
+    } else {
+        borrow_mut(v);
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/borrowck-lend-flow-loop.rs b/src/test/compile-fail/borrowck-lend-flow-loop.rs
new file mode 100644
index 00000000000..b6384ad9590
--- /dev/null
+++ b/src/test/compile-fail/borrowck-lend-flow-loop.rs
@@ -0,0 +1,164 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// 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
+// either genuine or would require more advanced changes.  The latter
+// cases are noted.
+
+fn borrow(_v: &int) {}
+fn borrow_mut(_v: &mut int) {}
+fn cond() -> bool { fail!() }
+fn for_func(_f: &fn() -> bool) { fail!() }
+fn produce<T>() -> T { fail!(); }
+
+fn inc(v: &mut ~int) {
+    *v = ~(**v + 1);
+}
+
+fn loop_overarching_alias_mut() {
+    // In this instance, the borrow encompasses the entire loop.
+
+    let mut v = ~3;
+    let mut x = &mut v;
+    **x += 1;
+    loop {
+        borrow(v); //~ ERROR cannot borrow
+    }
+}
+
+fn block_overarching_alias_mut() {
+    // In this instance, the borrow encompasses the entire closure call.
+
+    let mut v = ~3;
+    let mut x = &mut v;
+    for 3.times {
+        borrow(v); //~ ERROR cannot borrow
+    }
+    *x = ~5;
+}
+
+fn loop_aliased_mut() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    loop {
+        borrow_mut(v); //~ ERROR cannot borrow
+        _x = &v;
+    }
+}
+
+fn while_aliased_mut() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    while cond() {
+        borrow_mut(v); //~ ERROR cannot borrow
+        _x = &v;
+    }
+}
+
+fn for_loop_aliased_mut() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    for for_func {
+        borrow_mut(v); //~ ERROR cannot borrow
+        _x = &v;
+    }
+}
+
+fn loop_aliased_mut_break() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    loop {
+        borrow_mut(v);
+        _x = &v;
+        break;
+    }
+    borrow_mut(v); //~ ERROR cannot borrow
+}
+
+fn while_aliased_mut_break() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    while cond() {
+        borrow_mut(v);
+        _x = &v;
+        break;
+    }
+    borrow_mut(v); //~ ERROR cannot borrow
+}
+
+fn for_aliased_mut_break() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    for for_func {
+        // here we cannot be sure that `for_func` respects the break below
+        borrow_mut(v); //~ ERROR cannot borrow
+        _x = &v;
+        break;
+    }
+    borrow_mut(v); //~ ERROR cannot borrow
+}
+
+fn while_aliased_mut_cond(cond: bool, cond2: bool) {
+    let mut v = ~3, w = ~4;
+    let mut x = &mut w;
+    while cond {
+        **x += 1;
+        borrow(v); //~ ERROR cannot borrow
+        if cond2 {
+            x = &mut v; //~ ERROR cannot borrow
+        }
+    }
+}
+
+fn loop_break_pops_scopes<'r>(_v: &'r mut [uint], f: &fn(&'r mut uint) -> bool) {
+    // Here we check that when you break out of an inner loop, the
+    // borrows that go out of scope as you exit the inner loop are
+    // removed from the bitset.
+
+    while cond() {
+        while cond() {
+            // this borrow is limited to the scope of `r`...
+            let r: &'r mut uint = produce();
+            if !f(&mut *r) {
+                break; // ...so it is not live as exit the `while` loop here
+            }
+        }
+    }
+}
+
+fn loop_loop_pops_scopes<'r>(_v: &'r mut [uint], f: &fn(&'r mut uint) -> bool) {
+    // Similar to `loop_break_pops_scopes` but for the `loop` keyword
+
+    while cond() {
+        while cond() {
+            // this borrow is limited to the scope of `r`...
+            let r: &'r mut uint = produce();
+            if !f(&mut *r) {
+                loop; // ...so it is not live as exit (and re-enter) the `while` loop here
+            }
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/borrowck-lend-flow-match.rs b/src/test/compile-fail/borrowck-lend-flow-match.rs
new file mode 100644
index 00000000000..7603fdc82a8
--- /dev/null
+++ b/src/test/compile-fail/borrowck-lend-flow-match.rs
@@ -0,0 +1,60 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-pretty -- comments are infaithfully preserved
+
+#[allow(unused_variable)];
+#[allow(dead_assignment)];
+
+fn cond() -> bool { fail!() }
+fn link<'a>(v: &'a uint, w: &mut &'a uint) -> bool { *w = v; true }
+
+fn separate_arms() {
+    // Here both arms perform assignments, but only is illegal.
+
+    let mut x = None;
+    match x {
+        None => {
+            // It is ok to reassign x here, because there is in
+            // fact no outstanding loan of x!
+            x = Some(0);
+        }
+        Some(ref _i) => {
+            x = Some(1); //~ ERROR cannot assign
+        }
+    }
+    copy x; // just to prevent liveness warnings
+}
+
+fn guard() {
+    // Here the guard performs a borrow. This borrow "infects" all
+    // subsequent arms (but not the prior ones).
+
+    let mut a = ~3;
+    let mut b = ~4;
+    let mut w = &*a;
+    match 22 {
+        _ if cond() => {
+            b = ~5;
+        }
+
+        _ if link(&*b, &mut w) => {
+            b = ~6; //~ ERROR cannot assign
+        }
+
+        _ => {
+            b = ~7; //~ ERROR cannot assign
+        }
+    }
+
+    b = ~8; //~ ERROR cannot assign
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/borrowck-lend-flow.rs b/src/test/compile-fail/borrowck-lend-flow.rs
index ed6446a6311..59cac0c5d95 100644
--- a/src/test/compile-fail/borrowck-lend-flow.rs
+++ b/src/test/compile-fail/borrowck-lend-flow.rs
@@ -15,96 +15,37 @@
 // cases are noted.
 
 fn borrow(_v: &int) {}
+fn borrow_mut(_v: &mut int) {}
+fn cond() -> bool { fail!() }
+fn for_func(_f: &fn() -> bool) { fail!() }
+fn produce<T>() -> T { fail!(); }
 
 fn inc(v: &mut ~int) {
     *v = ~(**v + 1);
 }
 
-fn post_aliased_const() {
-    let mut v = ~3;
-    borrow(v);
-    let _w = &const v;
-}
-
-fn post_aliased_mut() {
-    // SPURIOUS--flow
-    let mut v = ~3;
-    borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    let _w = &mut v; //~ NOTE prior loan as mutable granted here
-}
+fn pre_freeze() {
+    // In this instance, the freeze starts before the mut borrow.
 
-fn post_aliased_scope(cond: bool) {
     let mut v = ~3;
-    borrow(v);
-    if cond { inc(&mut v); }
+    let _w = &v;
+    borrow_mut(v); //~ ERROR cannot borrow
 }
 
-fn loop_overarching_alias_mut() {
-    let mut v = ~3;
-    let mut _x = &mut v; //~ NOTE prior loan as mutable granted here
-    loop {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    }
-}
+fn pre_const() {
+    // In this instance, the freeze starts before the mut borrow.
 
-fn block_overarching_alias_mut() {
     let mut v = ~3;
-    let mut _x = &mut v; //~ NOTE prior loan as mutable granted here
-    for 3.times {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    }
-}
-
-fn loop_aliased_mut() {
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    loop {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        _x = &mut v; //~ NOTE prior loan as mutable granted here
-    }
-}
-
-fn while_aliased_mut(cond: bool) {
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    while cond {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        _x = &mut v; //~ NOTE prior loan as mutable granted here
-    }
-}
-
-fn while_aliased_mut_cond(cond: bool, cond2: bool) {
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    while cond {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        if cond2 {
-            _x = &mut v; //~ NOTE prior loan as mutable granted here
-        }
-    }
-}
-
-fn loop_in_block() {
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    for uint::range(0u, 10u) |_i| {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        _x = &mut v; //~ NOTE prior loan as mutable granted here
-    }
+    let _w = &const v;
+    borrow_mut(v);
 }
 
-fn at_most_once_block() {
-    fn at_most_once(f: &fn()) { f() }
+fn post_freeze() {
+    // In this instance, the const alias starts after the borrow.
 
-    // Here, the borrow check has no way of knowing that the block is
-    // executed at most once.
-
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    do at_most_once {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        _x = &mut v; //~ NOTE prior loan as mutable granted here
-    }
+    let mut v = ~3;
+    borrow_mut(v);
+    let _w = &v;
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs
index 784fce1300f..50dd815d493 100644
--- a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs
+++ b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs
@@ -14,17 +14,17 @@ fn borrow(v: &int, f: &fn(x: &int)) {
 
 fn box_imm() {
     let v = ~3;
-    let _w = &v; //~ NOTE loan of immutable local variable granted here
+    let _w = &v;
     do task::spawn {
         debug!("v=%d", *v);
-        //~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan
+        //~^ ERROR cannot move `v` into closure
     }
 
     let v = ~3;
-    let _w = &v; //~ NOTE loan of immutable local variable granted here
+    let _w = &v;
     task::spawn(|| {
         debug!("v=%d", *v);
-        //~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan
+        //~^ ERROR cannot move
     });
 }
 
diff --git a/src/test/compile-fail/borrowck-loan-blocks-move.rs b/src/test/compile-fail/borrowck-loan-blocks-move.rs
index 3af77d2df7f..b9a79f4f3b1 100644
--- a/src/test/compile-fail/borrowck-loan-blocks-move.rs
+++ b/src/test/compile-fail/borrowck-loan-blocks-move.rs
@@ -13,8 +13,8 @@ fn take(_v: ~int) {
 
 fn box_imm() {
     let v = ~3;
-    let _w = &v; //~ NOTE loan of immutable local variable granted here
-    take(v); //~ ERROR moving out of immutable local variable prohibited due to outstanding loan
+    let _w = &v;
+    take(v); //~ ERROR cannot move out of `v` because it is borrowed
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs
index 14cb37d775c..f8415a38573 100644
--- a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs
+++ b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs
@@ -14,8 +14,8 @@ fn borrow(v: &int, f: &fn(x: &int)) {
 
 fn box_imm() {
     let mut v = ~3;
-    do borrow(v) |w| { //~ NOTE loan of mutable local variable granted here
-        v = ~4; //~ ERROR assigning to captured outer mutable variable in a stack closure prohibited due to outstanding loan
+    do borrow(v) |w| {
+        v = ~4; //~ ERROR cannot assign to `v` because it is borrowed
         assert!(*v == 3);
         assert!(*w == 4);
     }
diff --git a/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs b/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs
index a2ba5ad4891..f369bf208c2 100644
--- a/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs
+++ b/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs
@@ -22,13 +22,13 @@ use core::either::{Either, Left, Right};
 
     fn g() {
         let mut x: Either<int,float> = Left(3);
-        io::println(f(&mut x, &x).to_str()); //~ ERROR conflicts with prior loan
+        io::println(f(&mut x, &x).to_str()); //~ ERROR cannot borrow
     }
 
     fn h() {
         let mut x: Either<int,float> = Left(3);
         let y: &Either<int, float> = &x;
-        let z: &mut Either<int, float> = &mut x; //~ ERROR conflicts with prior loan
+        let z: &mut Either<int, float> = &mut x; //~ ERROR cannot borrow
         *z = *y;
     } 
 
diff --git a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
index a4ad7e69b33..8c9d3009e5e 100644
--- a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
+++ b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-struct Point { 
+struct Point {
     x: int,
     y: int,
 }
@@ -38,10 +38,10 @@ fn b() {
 
     // Here I create an outstanding loan and check that we get conflicts:
 
-    let q = &mut p; //~ NOTE prior loan as mutable granted here
+    let q = &mut p;
 
     p + 3;  // ok for pure fns
-    p.times(3); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    p.times(3); //~ ERROR cannot borrow `p`
 
     q.x += 1;
 }
diff --git a/src/test/compile-fail/borrowck-loan-rcvr.rs b/src/test/compile-fail/borrowck-loan-rcvr.rs
index 4473574926a..decf6228fc6 100644
--- a/src/test/compile-fail/borrowck-loan-rcvr.rs
+++ b/src/test/compile-fail/borrowck-loan-rcvr.rs
@@ -13,7 +13,6 @@ struct point { x: int, y: int }
 trait methods {
     fn impurem(&self);
     fn blockm(&self, f: &fn());
-    fn purem(&self);
 }
 
 impl methods for point {
@@ -21,9 +20,6 @@ impl methods for point {
     }
 
     fn blockm(&self, f: &fn()) { f() }
-
-    fn purem(&self) {
-    }
 }
 
 fn a() {
@@ -31,12 +27,11 @@ fn a() {
 
     // Here: it's ok to call even though receiver is mutable, because we
     // can loan it out.
-    p.purem();
     p.impurem();
 
     // But in this case we do not honor the loan:
-    do p.blockm { //~ NOTE loan of mutable local variable granted here
-        p.x = 10; //~ ERROR assigning to mutable field prohibited due to outstanding loan
+    do p.blockm {
+        p.x = 10; //~ ERROR cannot assign
     }
 }
 
@@ -45,20 +40,21 @@ fn b() {
 
     // Here I create an outstanding loan and check that we get conflicts:
 
-    let l = &mut p; //~ NOTE prior loan as mutable granted here
-    //~^ NOTE prior loan as mutable granted here
-
-    p.purem(); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    p.impurem(); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    let l = &mut p;
+    p.impurem(); //~ ERROR cannot borrow
 
     l.x += 1;
 }
 
 fn c() {
-    // Loaning @mut as & is considered legal due to dynamic checks:
+    // Loaning @mut as & is considered legal due to dynamic checks...
     let q = @mut point {x: 3, y: 4};
-    q.purem();
     q.impurem();
+
+    // ...but we still detect errors statically when we can.
+    do q.blockm {
+        q.x = 10; //~ ERROR cannot assign
+    }
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-loan-vec-content.rs b/src/test/compile-fail/borrowck-loan-vec-content.rs
index d27d690437a..6a8e64377aa 100644
--- a/src/test/compile-fail/borrowck-loan-vec-content.rs
+++ b/src/test/compile-fail/borrowck-loan-vec-content.rs
@@ -24,8 +24,8 @@ fn has_mut_vec_and_does_not_try_to_change_it() {
 
 fn has_mut_vec_but_tries_to_change_it() {
     let mut v = ~[1, 2, 3];
-    do takes_imm_elt(&v[0]) { //~ NOTE loan of mutable vec content granted here
-        v[1] = 4; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan
+    do takes_imm_elt(&v[0]) {
+        v[1] = 4; //~ ERROR cannot assign
     }
 }
 
diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs
index 18b4ce0640c..c199c879575 100644
--- a/src/test/compile-fail/borrowck-move-by-capture.rs
+++ b/src/test/compile-fail/borrowck-move-by-capture.rs
@@ -4,7 +4,7 @@ fn main() {
     let foo = ~3;
     let _pfoo = &foo;
     let _f: @fn() -> int = || *foo + 5;
-    //~^ ERROR by-move capture
+    //~^ ERROR cannot move `foo`
 
     let bar = ~3;
     let _g = || {
diff --git a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
index d0b0f51d0cf..e4e44982276 100644
--- a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
+++ b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
@@ -10,7 +10,7 @@
 
 fn main() {
     let x: int = 3;
-    let y: &mut int = &mut x; //~ ERROR illegal borrow
+    let y: &mut int = &mut x; //~ ERROR cannot borrow
     *y = 5;
     debug!(*y);
 }
diff --git a/src/test/compile-fail/borrowck-mut-boxed-vec.rs b/src/test/compile-fail/borrowck-mut-boxed-vec.rs
index d4c0b5a1e9b..a69edebce51 100644
--- a/src/test/compile-fail/borrowck-mut-boxed-vec.rs
+++ b/src/test/compile-fail/borrowck-mut-boxed-vec.rs
@@ -10,8 +10,8 @@
 
 fn main() {
     let v = @mut [ 1, 2, 3 ];
-    for v.each |_x| {   //~ ERROR illegal borrow
-        v[1] = 4;
+    for v.each |_x| {
+        v[1] = 4; //~ ERROR cannot assign
     }
 }
 
diff --git a/src/test/compile-fail/borrowck-mut-deref-comp.rs b/src/test/compile-fail/borrowck-mut-deref-comp.rs
index 540793d4135..d1dc2961978 100644
--- a/src/test/compile-fail/borrowck-mut-deref-comp.rs
+++ b/src/test/compile-fail/borrowck-mut-deref-comp.rs
@@ -11,8 +11,8 @@
 struct foo(~int);
 
 fn borrow(x: @mut foo) {
-    let _y = &***x; //~ ERROR illegal borrow unless pure
-    *x = foo(~4); //~ NOTE impure due to assigning to dereference of mutable @ pointer
+    let _y = &***x;
+    *x = foo(~4); //~ ERROR cannot assign
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
index bc0340983ae..ec17976c506 100644
--- a/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
+++ b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
@@ -14,5 +14,5 @@ fn write(v: &mut [int]) {
 
 fn main() {
     let v = ~[1, 2, 3];
-    write(v); //~ ERROR illegal borrow
+    write(v); //~ ERROR cannot borrow
 }
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
index 4af3bc17240..ed270de51e2 100644
--- a/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs
+++ b/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs
@@ -19,9 +19,9 @@ enum cycle {
 fn main() {
     let mut x = ~node(node_ {a: ~empty});
     // Create a cycle!
-    match *x { //~ NOTE loan of mutable local variable granted here
+    match *x {
       node(ref mut y) => {
-        y.a = x; //~ ERROR moving out of mutable local variable prohibited due to outstanding loan
+        y.a = x; //~ ERROR cannot move out of
       }
       empty => {}
     };
diff --git a/src/test/compile-fail/borrowck-pat-by-value-binding.rs b/src/test/compile-fail/borrowck-pat-by-value-binding.rs
index d8c8841d391..d60ed3d0e37 100644
--- a/src/test/compile-fail/borrowck-pat-by-value-binding.rs
+++ b/src/test/compile-fail/borrowck-pat-by-value-binding.rs
@@ -12,23 +12,24 @@ fn process<T>(_t: T) {}
 
 fn match_const_opt_by_mut_ref(v: &const Option<int>) {
     match *v {
-      Some(ref mut i) => process(i), //~ ERROR illegal borrow
+      Some(ref mut i) => process(i), //~ ERROR cannot borrow
+        //~^ ERROR unsafe borrow of aliasable, const value
       None => ()
     }
 }
 
 fn match_const_opt_by_const_ref(v: &const Option<int>) {
     match *v {
-      Some(ref const i) => process(i), //~ ERROR illegal borrow unless pure
-      //~^ NOTE impure due to
+      Some(ref const i) => process(i),
+        //~^ ERROR unsafe borrow of aliasable, const value
       None => ()
     }
 }
 
 fn match_const_opt_by_imm_ref(v: &const Option<int>) {
     match *v {
-      Some(ref i) => process(i), //~ ERROR illegal borrow unless pure
-      //~^ NOTE impure due to
+      Some(ref i) => process(i), //~ ERROR cannot borrow
+        //~^ ERROR unsafe borrow of aliasable, const value
       None => ()
     }
 }
diff --git a/src/test/compile-fail/borrowck-pat-enum.rs b/src/test/compile-fail/borrowck-pat-enum.rs
index 4aa1ecc0ce3..c50357e8b9c 100644
--- a/src/test/compile-fail/borrowck-pat-enum.rs
+++ b/src/test/compile-fail/borrowck-pat-enum.rs
@@ -26,7 +26,8 @@ fn match_ref_unused(&&v: Option<int>) {
 
 fn match_const_reg(v: &const Option<int>) -> int {
     match *v {
-      Some(ref i) => {*i} // OK because this is pure
+      Some(ref i) => {*i} //~ ERROR cannot borrow
+        //~^ ERROR unsafe borrow
       None => {0}
     }
 }
@@ -43,8 +44,8 @@ fn match_const_reg_unused(v: &const Option<int>) {
 
 fn match_const_reg_impure(v: &const Option<int>) {
     match *v {
-      Some(ref i) => {impure(*i)} //~ ERROR illegal borrow unless pure
-      //~^ NOTE impure due to access to impure function
+      Some(ref i) => {impure(*i)} //~ ERROR cannot borrow
+        //~^ ERROR unsafe borrow
       None => {}
     }
 }
@@ -56,5 +57,12 @@ fn match_imm_reg(v: &Option<int>) {
     }
 }
 
+fn match_mut_reg(v: &mut Option<int>) {
+    match *v {
+      Some(ref i) => {impure(*i)} // OK, frozen
+      None => {}
+    }
+}
+
 fn main() {
 }
diff --git a/src/test/compile-fail/borrowck-pat-reassign-binding.rs b/src/test/compile-fail/borrowck-pat-reassign-binding.rs
index ca1fdc97c22..d05160132c6 100644
--- a/src/test/compile-fail/borrowck-pat-reassign-binding.rs
+++ b/src/test/compile-fail/borrowck-pat-reassign-binding.rs
@@ -12,11 +12,14 @@
 
 fn main() {
     let mut x: Option<int> = None;
-    match x { //~ NOTE loan of mutable local variable granted here
-      None => {}
+    match x {
+      None => {
+          // Note: on this branch, no borrow has occurred.
+          x = Some(0);
+      }
       Some(ref i) => {
-        // Not ok: i is an outstanding ptr into x.
-        x = Some(*i+1); //~ ERROR assigning to mutable local variable prohibited due to outstanding loan
+          // But on this branch, `i` is an outstanding borrow
+          x = Some(*i+1); //~ ERROR cannot assign to `x`
       }
     }
     copy x; // just to prevent liveness warnings
diff --git a/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs b/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs
deleted file mode 100644
index dd6eca951b8..00000000000
--- a/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// xfail-pretty -- comments are infaithfully preserved
-
-fn main() {
-    let mut x = None;
-    match x { //~ NOTE loan of mutable local variable granted here
-      None => {
-        // It is ok to reassign x here, because there is in
-        // fact no outstanding loan of x!
-        x = Some(0);
-      }
-      Some(ref _i) => {
-        x = Some(1); //~ ERROR assigning to mutable local variable prohibited due to outstanding loan
-      }
-    }
-    copy x; // just to prevent liveness warnings
-}
diff --git a/src/test/compile-fail/borrowck-reborrow-from-mut.rs b/src/test/compile-fail/borrowck-reborrow-from-mut.rs
index 60f817dee0c..b4bd64f2135 100644
--- a/src/test/compile-fail/borrowck-reborrow-from-mut.rs
+++ b/src/test/compile-fail/borrowck-reborrow-from-mut.rs
@@ -20,17 +20,17 @@ struct Bar {
 
 fn borrow_same_field_twice_mut_mut(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
 }
 
 fn borrow_same_field_twice_mut_imm(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1;
-    let _bar2 = &foo.bar1;  //~ ERROR conflicts with prior loan
+    let _bar2 = &foo.bar1;  //~ ERROR cannot borrow
 }
 
 fn borrow_same_field_twice_imm_mut(foo: &mut Foo) {
     let _bar1 = &foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
 }
 
 fn borrow_same_field_twice_imm_imm(foo: &mut Foo) {
@@ -53,34 +53,34 @@ fn borrow_var_and_pattern(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1;
     match *foo {
         Foo { bar1: ref mut _bar1, bar2: _ } => {}
-        //~^ ERROR conflicts with prior loan
+        //~^ ERROR cannot borrow
     }
 }
 
 fn borrow_mut_and_base_imm(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan
-    let _foo2 = &*foo; //~ ERROR conflicts with prior loan
+    let _foo1 = &foo.bar1; //~ ERROR cannot borrow
+    let _foo2 = &*foo; //~ ERROR cannot borrow
 }
 
 fn borrow_mut_and_base_mut(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
 }
 
 fn borrow_mut_and_base_mut2(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1.int1;
-    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+    let _foo2 = &mut *foo; //~ ERROR cannot borrow
 }
 
 fn borrow_imm_and_base_mut(foo: &mut Foo) {
     let _bar1 = &foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
 }
 
 fn borrow_imm_and_base_mut2(foo: &mut Foo) {
     let _bar1 = &foo.bar1.int1;
-    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+    let _foo2 = &mut *foo; //~ ERROR cannot borrow
 }
 
 fn borrow_imm_and_base_imm(foo: &mut Foo) {
@@ -95,7 +95,7 @@ fn borrow_mut_and_imm(foo: &mut Foo) {
 }
 
 fn borrow_mut_from_imm(foo: &Foo) {
-    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+    let _bar1 = &mut foo.bar1; //~ ERROR cannot borrow
 }
 
 fn borrow_long_path_both_mut(foo: &mut Foo) {
diff --git a/src/test/compile-fail/borrowck-ref-into-rvalue.rs b/src/test/compile-fail/borrowck-ref-into-rvalue.rs
index 37ee747069c..18865ad7d54 100644
--- a/src/test/compile-fail/borrowck-ref-into-rvalue.rs
+++ b/src/test/compile-fail/borrowck-ref-into-rvalue.rs
@@ -10,12 +10,12 @@
 
 fn main() {
     let msg;
-    match Some(~"Hello") { //~ ERROR illegal borrow
-        Some(ref m) => {
+    match Some(~"Hello") {
+        Some(ref m) => { //~ ERROR borrowed value does not live long enough
             msg = m;
-        },  
+        },
         None => { fail!() }
-    }   
+    }
     io::println(*msg);
 }
 
diff --git a/src/test/compile-fail/borrowck-ref-mut-of-imm.rs b/src/test/compile-fail/borrowck-ref-mut-of-imm.rs
index aad86241e9a..3a37116a166 100644
--- a/src/test/compile-fail/borrowck-ref-mut-of-imm.rs
+++ b/src/test/compile-fail/borrowck-ref-mut-of-imm.rs
@@ -11,7 +11,7 @@
 fn destructure(x: Option<int>) -> int {
     match x {
       None => 0,
-      Some(ref mut v) => *v //~ ERROR illegal borrow
+      Some(ref mut v) => *v //~ ERROR cannot borrow
     }
 }
 
diff --git a/src/test/compile-fail/borrowck-unary-move-2.rs b/src/test/compile-fail/borrowck-unary-move-2.rs
index 520772f1cee..898830bbe55 100644
--- a/src/test/compile-fail/borrowck-unary-move-2.rs
+++ b/src/test/compile-fail/borrowck-unary-move-2.rs
@@ -28,5 +28,5 @@ struct wrapper(noncopyable);
 
 fn main() {
     let x1 = wrapper(noncopyable());
-    let _x2 = *x1; //~ ERROR moving out of anonymous field
+    let _x2 = *x1; //~ ERROR cannot move out
 }
diff --git a/src/test/compile-fail/borrowck-unary-move.rs b/src/test/compile-fail/borrowck-unary-move.rs
index f95b365ee2e..107e478004a 100644
--- a/src/test/compile-fail/borrowck-unary-move.rs
+++ b/src/test/compile-fail/borrowck-unary-move.rs
@@ -9,8 +9,8 @@
 // except according to those terms.
 
 fn foo(+x: ~int) -> int {
-    let y = &*x; //~ NOTE loan of argument granted here
-    free(x); //~ ERROR moving out of argument prohibited due to outstanding loan
+    let y = &*x;
+    free(x); //~ ERROR cannot move out of `*x` because it is borrowed
     *y
 }
 
diff --git a/src/test/compile-fail/borrowck-uniq-via-box.rs b/src/test/compile-fail/borrowck-uniq-via-box.rs
deleted file mode 100644
index e1c0e67ff8d..00000000000
--- a/src/test/compile-fail/borrowck-uniq-via-box.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-struct Rec {
-    f: ~int,
-}
-
-struct Outer {
-    f: Inner
-}
-
-struct Inner {
-    g: Innermost
-}
-
-struct Innermost {
-    h: ~int,
-}
-
-fn borrow(_v: &int) {}
-
-fn box_mut(v: @mut ~int) {
-    borrow(*v); //~ ERROR illegal borrow unless pure
-}
-
-fn box_mut_rec(v: @mut Rec) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure
-}
-
-fn box_mut_recs(v: @mut Outer) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
-}
-
-fn box_imm(v: @~int) {
-    borrow(*v); // OK
-}
-
-fn box_imm_rec(v: @Rec) {
-    borrow(v.f); // OK
-}
-
-fn box_imm_recs(v: @Outer) {
-    borrow(v.f.g.h); // OK
-}
-
-fn main() {
-}
-
diff --git a/src/test/compile-fail/borrowck-uniq-via-lend.rs b/src/test/compile-fail/borrowck-uniq-via-lend.rs
index ee96237a26c..80ba1968bc7 100644
--- a/src/test/compile-fail/borrowck-uniq-via-lend.rs
+++ b/src/test/compile-fail/borrowck-uniq-via-lend.rs
@@ -43,8 +43,8 @@ fn aliased_const() {
 
 fn aliased_mut() {
     let mut v = ~3;
-    let _w = &mut v; //~ NOTE prior loan as mutable granted here
-    borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    let _w = &mut v;
+    borrow(v); //~ ERROR cannot borrow `*v`
 }
 
 fn aliased_other() {
@@ -56,8 +56,8 @@ fn aliased_other() {
 fn aliased_other_reassign() {
     let mut v = ~3, w = ~4;
     let mut _x = &mut w;
-    _x = &mut v; //~ NOTE prior loan as mutable granted here
-    borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    _x = &mut v;
+    borrow(v); //~ ERROR cannot borrow `*v`
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-uniq-via-ref.rs b/src/test/compile-fail/borrowck-uniq-via-ref.rs
index 2cf363e13ee..8bf627d9919 100644
--- a/src/test/compile-fail/borrowck-uniq-via-ref.rs
+++ b/src/test/compile-fail/borrowck-uniq-via-ref.rs
@@ -25,6 +25,7 @@ struct Innermost {
 }
 
 fn borrow(_v: &int) {}
+fn borrow_const(_v: &const int) {}
 
 fn box_mut(v: &mut ~int) {
     borrow(*v); // OK: &mut -> &imm
@@ -51,15 +52,15 @@ fn box_imm_recs(v: &Outer) {
 }
 
 fn box_const(v: &const ~int) {
-    borrow(*v); //~ ERROR illegal borrow unless pure
+    borrow_const(*v); //~ ERROR unsafe borrow
 }
 
 fn box_const_rec(v: &const Rec) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure
+    borrow_const(v.f); //~ ERROR unsafe borrow
 }
 
 fn box_const_recs(v: &const Outer) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
+    borrow_const(v.f.g.h); //~ ERROR unsafe borrow
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs
index c8a0dbedd5d..0c21b64bb0f 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs
@@ -1,7 +1,7 @@
 fn a() -> &[int] {
     let vec = [1, 2, 3, 4];
-    let tail = match vec { //~ ERROR illegal borrow
-        [_, ..tail] => tail,
+    let tail = match vec {
+        [_, ..tail] => tail, //~ ERROR does not live long enough
         _ => fail!(~"a")
     };
     tail
@@ -9,8 +9,8 @@ fn a() -> &[int] {
 
 fn b() -> &[int] {
     let vec = [1, 2, 3, 4];
-    let init = match vec { //~ ERROR illegal borrow
-        [..init, _] => init,
+    let init = match vec {
+        [..init, _] => init, //~ ERROR does not live long enough
         _ => fail!(~"b")
     };
     init
@@ -18,8 +18,8 @@ fn b() -> &[int] {
 
 fn c() -> &[int] {
     let vec = [1, 2, 3, 4];
-    let slice = match vec { //~ ERROR illegal borrow
-        [_, ..slice, _] => slice,
+    let slice = match vec {
+        [_, ..slice, _] => slice, //~ ERROR does not live long enough
         _ => fail!(~"c")
     };
     slice
diff --git a/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs
index 27902100373..6b51fc8f5b3 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs
@@ -2,7 +2,7 @@ fn a() {
     let mut v = ~[1, 2, 3];
     match v {
         [_a, ..tail] => {
-            v.push(tail[0] + tail[1]); //~ ERROR conflicts with prior loan
+            v.push(tail[0] + tail[1]); //~ ERROR cannot borrow
         }
         _ => {}
     };
diff --git a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs
index 16b48aedb0c..2898e312930 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs
@@ -1,8 +1,9 @@
 fn main() {
     let mut a = [1, 2, 3, 4];
-    let _ = match a {
+    let t = match a {
         [1, 2, ..tail] => tail,
         _ => core::util::unreachable()
     };
-    a[0] = 0; //~ ERROR: assigning to mutable vec content prohibited due to outstanding loan
+    a[0] = 0; //~ ERROR cannot assign to `a[]` because it is borrowed
+    t[0];
 }
diff --git a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs
index 05ff85d612c..16711ea61ff 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs
@@ -2,7 +2,7 @@ fn a() {
     let mut vec = [~1, ~2, ~3];
     match vec {
         [~ref _a] => {
-            vec[0] = ~4; //~ ERROR prohibited due to outstanding loan
+            vec[0] = ~4; //~ ERROR cannot assign to `vec[]` because it is borrowed
         }
         _ => fail!(~"foo")
     }
@@ -12,7 +12,7 @@ fn b() {
     let mut vec = [~1, ~2, ~3];
     match vec {
         [.._b] => {
-            vec[0] = ~4; //~ ERROR prohibited due to outstanding loan
+            vec[0] = ~4; //~ ERROR cannot assign to `vec[]` because it is borrowed
         }
     }
 }
diff --git a/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs
index 714a80def93..dbdd8f0809a 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs
@@ -1,7 +1,7 @@
 fn a() -> &int {
     let vec = [1, 2, 3, 4];
-    let tail = match vec { //~ ERROR illegal borrow
-        [_a, ..tail] => &tail[0],
+    let tail = match vec {
+        [_a, ..tail] => &tail[0], //~ ERROR borrowed value does not live long enough
         _ => fail!(~"foo")
     };
     tail
diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs
index e47ad721b0d..5ef23897ec0 100644
--- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs
+++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs
@@ -1,6 +1,6 @@
 fn main() {
     let mut b = ~3;
-    let _x = &mut *b;   //~ NOTE prior loan as mutable granted here
-    let _y = &mut *b;   //~ ERROR loan of dereference of mutable ~ pointer as mutable conflicts with prior loan
+    let _x = &mut *b;
+    let _y = &mut *b; //~ ERROR cannot borrow
 }
 
diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs
index 015f368ecb0..a744127a600 100644
--- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs
+++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs
@@ -1,8 +1,8 @@
 fn main() {
     let mut a = ~3;
-    let mut b = &mut a; //~ NOTE loan of mutable local variable granted here
+    let mut b = &mut a;
     let _c = &mut *b;
-    let mut d = /*move*/ a; //~ ERROR moving out of mutable local variable prohibited due to outstanding loan
+    let mut d = /*move*/ a; //~ ERROR cannot move out
     *d += 1;
 }
 
diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs
index 36d32fddda1..44da00d8d17 100644
--- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs
+++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs
@@ -1,7 +1,7 @@
 fn main() {
     let mut b = ~3;
-    let _x = &mut *b;   //~ NOTE loan of mutable local variable granted here
-    let mut y = /*move*/ b; //~ ERROR moving out of mutable local variable prohibited
+    let _x = &mut *b;
+    let mut y = /*move*/ b; //~ ERROR cannot move out
     *y += 1;
 }
 
diff --git a/src/test/compile-fail/borrowck-wg-move-base-2.rs b/src/test/compile-fail/borrowck-wg-move-base-2.rs
index ba85616e63f..8a218771453 100644
--- a/src/test/compile-fail/borrowck-wg-move-base-2.rs
+++ b/src/test/compile-fail/borrowck-wg-move-base-2.rs
@@ -2,7 +2,7 @@ fn foo(x: &mut int) {
     let mut a = 3;
     let mut _y = &mut *x;
     let _z = &mut *_y;
-    _y = &mut a; //~ ERROR assigning to mutable local variable prohibited
+    _y = &mut a; //~ ERROR cannot assign
 }
 
 fn main() {
diff --git a/src/test/compile-fail/fn-variance-3.rs b/src/test/compile-fail/fn-variance-3.rs
index 5df2007721d..4d145d3f9ea 100644
--- a/src/test/compile-fail/fn-variance-3.rs
+++ b/src/test/compile-fail/fn-variance-3.rs
@@ -31,5 +31,5 @@ fn main() {
     // mutability check will fail, because the
     // type of r has been inferred to be
     // fn(@const int) -> @const int
-    *r(@mut 3) = 4; //~ ERROR assigning to dereference of const @ pointer
+    *r(@mut 3) = 4; //~ ERROR cannot assign to const dereference of @ pointer
 }
diff --git a/src/test/compile-fail/immut-function-arguments.rs b/src/test/compile-fail/immut-function-arguments.rs
index 2084729372d..66b5bd172ca 100644
--- a/src/test/compile-fail/immut-function-arguments.rs
+++ b/src/test/compile-fail/immut-function-arguments.rs
@@ -9,11 +9,11 @@
 // except according to those terms.
 
 fn f(y: ~int) {
-    *y = 5; //~ ERROR assigning to dereference of immutable ~ pointer
+    *y = 5; //~ ERROR cannot assign
 }
 
 fn g() {
-    let _frob: &fn(~int) = |q| { *q = 2; }; //~ ERROR assigning to dereference of immutable ~ pointer
+    let _frob: &fn(~int) = |q| { *q = 2; }; //~ ERROR cannot assign
 
 }
 
diff --git a/src/test/compile-fail/index_message.rs b/src/test/compile-fail/index_message.rs
index 3611dbb6866..26dd98757a8 100644
--- a/src/test/compile-fail/index_message.rs
+++ b/src/test/compile-fail/index_message.rs
@@ -10,5 +10,5 @@
 
 fn main() {
     let z = ();
-    debug!(z[0]); //~ ERROR cannot index a value of type `()`
+    let _ = z[0]; //~ ERROR cannot index a value of type `()`
 }
diff --git a/src/test/compile-fail/issue-1896-1.rs b/src/test/compile-fail/issue-1896-1.rs
index fc5132d6510..13adcd42da2 100644
--- a/src/test/compile-fail/issue-1896-1.rs
+++ b/src/test/compile-fail/issue-1896-1.rs
@@ -8,11 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// Test that we require managed closures to be rooted when borrowed.
+
 struct boxedFn<'self> { theFn: &'self fn() -> uint }
 
 fn createClosure (closedUint: uint) -> boxedFn {
     let theFn: @fn() -> uint = || closedUint;
-    boxedFn {theFn: theFn} //~ ERROR illegal borrow
+    boxedFn {theFn: theFn} //~ ERROR cannot root
 }
 
 fn main () {
diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs
index 2842d884c99..cdc8d546dd8 100644
--- a/src/test/compile-fail/issue-2149.rs
+++ b/src/test/compile-fail/issue-2149.rs
@@ -23,5 +23,4 @@ impl<A> vec_monad<A> for ~[A] {
 fn main() {
     ["hi"].bind(|x| [x] );
     //~^ ERROR type `[&'static str, .. 1]` does not implement any method in scope named `bind`
-    //~^^ ERROR Unconstrained region variable
 }
diff --git a/src/test/compile-fail/issue-2151.rs b/src/test/compile-fail/issue-2151.rs
index e2bbda7d65a..bb6d47a4762 100644
--- a/src/test/compile-fail/issue-2151.rs
+++ b/src/test/compile-fail/issue-2151.rs
@@ -10,7 +10,6 @@
 
 fn main() {
     for vec::each(fail!()) |i| {
-        debug!(i * 2);
-        //~^ ERROR the type of this value must be known
+        let _ = i * 2; //~ ERROR the type of this value must be known
    };
 }
diff --git a/src/test/compile-fail/issue-2590.rs b/src/test/compile-fail/issue-2590.rs
index 7a99ab8a94f..a0b967d5959 100644
--- a/src/test/compile-fail/issue-2590.rs
+++ b/src/test/compile-fail/issue-2590.rs
@@ -18,7 +18,7 @@ trait parse {
 
 impl parse for parser {
     fn parse(&self) -> ~[int] {
-        self.tokens //~ ERROR moving out of immutable field
+        self.tokens //~ ERROR cannot move out of field
     }
 }
 
diff --git a/src/test/compile-fail/issue-3044.rs b/src/test/compile-fail/issue-3044.rs
index fcd5b1deee5..23b11bbe409 100644
--- a/src/test/compile-fail/issue-3044.rs
+++ b/src/test/compile-fail/issue-3044.rs
@@ -11,7 +11,6 @@
 fn main() {
     let needlesArr: ~[char] = ~['a', 'f'];
     do vec::foldr(needlesArr) |x, y| {
-        //~^ ERROR Unconstrained region variable #2
     }
     //~^ ERROR 2 parameters were supplied (including the closure passed by the `do` keyword)
     //
diff --git a/src/test/compile-fail/issue-511.rs b/src/test/compile-fail/issue-511.rs
index 90c46e5d602..c872f89d884 100644
--- a/src/test/compile-fail/issue-511.rs
+++ b/src/test/compile-fail/issue-511.rs
@@ -17,5 +17,5 @@ fn f<T:Eq>(o: &mut Option<T>) {
 
 fn main() {
     f::<int>(&mut option::None);
-    //~^ ERROR illegal borrow: creating mutable alias to static item
+    //~^ ERROR cannot borrow
 }
diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs
index 54ee8bcc70e..6bb90bff228 100644
--- a/src/test/compile-fail/kindck-owned-trait-contains.rs
+++ b/src/test/compile-fail/kindck-owned-trait-contains.rs
@@ -29,4 +29,7 @@ fn main() {
     };
     assert!(3 == *(y.get())); //~ ERROR dereference of reference outside its lifetime
     //~^ ERROR reference is not valid outside of its lifetime
+    //~^^ ERROR reference is not valid outside of its lifetime
+    //~^^^ ERROR reference is not valid outside of its lifetime
+    //~^^^^ ERROR cannot infer an appropriate lifetime
 }
diff --git a/src/test/compile-fail/lambda-mutate-nested.rs b/src/test/compile-fail/lambda-mutate-nested.rs
index 8b009b91af9..bfd1e12f3a6 100644
--- a/src/test/compile-fail/lambda-mutate-nested.rs
+++ b/src/test/compile-fail/lambda-mutate-nested.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to captured outer immutable variable in a stack closure
 // Make sure that nesting a block within a @fn doesn't let us
 // mutate upvars from a @fn.
 fn f2(x: &fn()) { x(); }
@@ -16,6 +15,7 @@ fn f2(x: &fn()) { x(); }
 fn main() {
     let i = 0;
     let ctr: @fn() -> int = || { f2(|| i = i + 1 ); i };
+    //~^ ERROR cannot assign
     error!(ctr());
     error!(ctr());
     error!(ctr());
diff --git a/src/test/compile-fail/lambda-mutate.rs b/src/test/compile-fail/lambda-mutate.rs
index ee5b3d89684..a848d8698a3 100644
--- a/src/test/compile-fail/lambda-mutate.rs
+++ b/src/test/compile-fail/lambda-mutate.rs
@@ -8,11 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to captured outer variable in a heap closure
 // Make sure we can't write to upvars from @fns
 fn main() {
     let i = 0;
     let ctr: @fn() -> int = || { i = i + 1; i };
+    //~^ ERROR cannot assign
     error!(ctr());
     error!(ctr());
     error!(ctr());
diff --git a/src/test/compile-fail/moves-based-on-type-block-bad.rs b/src/test/compile-fail/moves-based-on-type-block-bad.rs
index 020dadfc96c..feeaadeea82 100644
--- a/src/test/compile-fail/moves-based-on-type-block-bad.rs
+++ b/src/test/compile-fail/moves-based-on-type-block-bad.rs
@@ -16,7 +16,7 @@ fn main() {
     let s = S { x: ~Bar(~42) };
     loop {
         do f(&s) |hellothere| {
-            match hellothere.x {    //~ ERROR moving out of immutable field
+            match hellothere.x {    //~ ERROR cannot move out
                 ~Foo(_) => {}
                 ~Bar(x) => io::println(x.to_str()),
                 ~Baz => {}
diff --git a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs
index 3c15047a296..ecd58d485a8 100644
--- a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs
+++ b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs
@@ -13,6 +13,6 @@ fn test(_x: ~uint) {}
 fn main() {
     let i = ~3;
     for uint::range(0, 10) |_x| {
-        test(i); //~ ERROR moving out of captured outer immutable variable in a stack closure
+        test(i); //~ ERROR cannot move out
     }
 }
diff --git a/src/test/compile-fail/mutable-class-fields-2.rs b/src/test/compile-fail/mutable-class-fields-2.rs
index 56c715c9847..f5d24b31641 100644
--- a/src/test/compile-fail/mutable-class-fields-2.rs
+++ b/src/test/compile-fail/mutable-class-fields-2.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to immutable field
 struct cat {
   priv mut meows : uint,
 
@@ -17,7 +16,7 @@ struct cat {
 
 pub impl cat {
   fn eat(&self) {
-    self.how_hungry -= 5;
+    self.how_hungry -= 5; //~ ERROR cannot assign
   }
 
 }
diff --git a/src/test/compile-fail/mutable-class-fields.rs b/src/test/compile-fail/mutable-class-fields.rs
index 6d11a98c0cb..8bebec7134c 100644
--- a/src/test/compile-fail/mutable-class-fields.rs
+++ b/src/test/compile-fail/mutable-class-fields.rs
@@ -8,12 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to immutable field
 struct cat {
   priv mut meows : uint,
-
   how_hungry : int,
-
 }
 
 fn cat(in_x : uint, in_y : int) -> cat {
@@ -25,5 +22,5 @@ fn cat(in_x : uint, in_y : int) -> cat {
 
 fn main() {
   let nyan : cat = cat(52u, 99);
-  nyan.how_hungry = 0;
+  nyan.how_hungry = 0; //~ ERROR cannot assign
 }
diff --git a/src/test/compile-fail/mutable-huh-ptr-assign.rs b/src/test/compile-fail/mutable-huh-ptr-assign.rs
index ed356f4001d..6b3fd4f7153 100644
--- a/src/test/compile-fail/mutable-huh-ptr-assign.rs
+++ b/src/test/compile-fail/mutable-huh-ptr-assign.rs
@@ -12,7 +12,7 @@ extern mod std;
 
 fn main() {
     unsafe fn f(&&v: *const int) {
-        *v = 1 //~ ERROR assigning to dereference of const * pointer
+        *v = 1 //~ ERROR cannot assign
     }
 
     unsafe {
diff --git a/src/test/compile-fail/regions-addr-of-arg.rs b/src/test/compile-fail/regions-addr-of-arg.rs
index 7f2140d96e1..4fff5a6f87c 100644
--- a/src/test/compile-fail/regions-addr-of-arg.rs
+++ b/src/test/compile-fail/regions-addr-of-arg.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn foo(a: int) {
-    let _p: &'static int = &a; //~ ERROR illegal borrow
+    let _p: &'static int = &a; //~ ERROR borrowed value does not live long enough
 }
 
 fn bar(a: int) {
diff --git a/src/test/compile-fail/regions-creating-enums.rs b/src/test/compile-fail/regions-creating-enums.rs
index 120428e02f4..2ab0c14b49b 100644
--- a/src/test/compile-fail/regions-creating-enums.rs
+++ b/src/test/compile-fail/regions-creating-enums.rs
@@ -30,12 +30,12 @@ fn compute(x: &ast) -> uint {
 fn map_nums(x: &ast, f: &fn(uint) -> uint) -> &ast {
     match *x {
       num(x) => {
-        return &num(f(x)); //~ ERROR illegal borrow
+        return &num(f(x)); //~ ERROR borrowed value does not live long enough
       }
       add(x, y) => {
         let m_x = map_nums(x, f);
         let m_y = map_nums(y, f);
-        return &add(m_x, m_y);  //~ ERROR illegal borrow
+        return &add(m_x, m_y);  //~ ERROR borrowed value does not live long enough
       }
     }
 }
diff --git a/src/test/compile-fail/regions-creating-enums4.rs b/src/test/compile-fail/regions-creating-enums4.rs
index 1cb378cf406..8f764745697 100644
--- a/src/test/compile-fail/regions-creating-enums4.rs
+++ b/src/test/compile-fail/regions-creating-enums4.rs
@@ -14,8 +14,7 @@ enum ast<'self> {
 }
 
 fn mk_add_bad2<'a>(x: &'a ast<'a>, y: &'a ast<'a>, z: &ast) -> ast {
-    add(x, y)
-         //~^ ERROR cannot infer an appropriate lifetime
+    add(x, y) //~ ERROR cannot infer an appropriate lifetime
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-escape-bound-fn.rs b/src/test/compile-fail/regions-escape-bound-fn.rs
index c81ef77f497..5ac5e334be2 100644
--- a/src/test/compile-fail/regions-escape-bound-fn.rs
+++ b/src/test/compile-fail/regions-escape-bound-fn.rs
@@ -14,6 +14,6 @@ fn with_int(f: &fn(x: &int)) {
 }
 
 fn main() {
-    let mut x: Option<&int> = None; //~ ERROR cannot infer
+    let mut x: Option<&int> = None;   //~ ERROR cannot infer
     with_int(|y| x = Some(y));
 }
diff --git a/src/test/compile-fail/regions-escape-loop-via-variable.rs b/src/test/compile-fail/regions-escape-loop-via-variable.rs
index ac10b5c454a..19bd0bf9747 100644
--- a/src/test/compile-fail/regions-escape-loop-via-variable.rs
+++ b/src/test/compile-fail/regions-escape-loop-via-variable.rs
@@ -18,6 +18,6 @@ fn main() {
 
     loop {
         let x = 1 + *p;
-        p = &x; //~ ERROR illegal borrow
+        p = &x; //~ ERROR borrowed value does not live long enough
     }
 }
diff --git a/src/test/compile-fail/regions-escape-loop-via-vec.rs b/src/test/compile-fail/regions-escape-loop-via-vec.rs
index da5e3c2660e..92e2cd73dfb 100644
--- a/src/test/compile-fail/regions-escape-loop-via-vec.rs
+++ b/src/test/compile-fail/regions-escape-loop-via-vec.rs
@@ -14,8 +14,8 @@ fn broken() {
     let mut _y = ~[&mut x];
     while x < 10 {
         let mut z = x;
-        _y.push(&mut z); //~ ERROR illegal borrow
-        x += 1; //~ ERROR assigning to mutable local variable prohibited due to outstanding loan
+        _y.push(&mut z); //~ ERROR borrowed value does not live long enough
+        x += 1; //~ ERROR cannot assign
     }
 }
 
diff --git a/src/test/compile-fail/regions-escape-via-trait-or-not.rs b/src/test/compile-fail/regions-escape-via-trait-or-not.rs
index f7165784c79..aa431d6b81c 100644
--- a/src/test/compile-fail/regions-escape-via-trait-or-not.rs
+++ b/src/test/compile-fail/regions-escape-via-trait-or-not.rs
@@ -23,13 +23,8 @@ fn with<R:deref>(f: &fn(x: &int) -> R) -> int {
 }
 
 fn return_it() -> int {
-    with(|o| o)
-    //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements
-    //~^^ ERROR reference is not valid outside of its lifetime
-    //~^^^ ERROR reference is not valid outside of its lifetime
+    with(|o| o) //~ ERROR reference is not valid outside of its lifetime
 }
 
 fn main() {
-    let x = return_it();
-    debug!("foo=%d", x);
 }
diff --git a/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
index a8b7ae1b9c8..6e9d6c1ef0f 100644
--- a/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
+++ b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
@@ -18,7 +18,7 @@ fn x_coord<'r>(p: &'r point) -> &'r int {
 }
 
 fn foo(p: @point) -> &int {
-    let xc = x_coord(p); //~ ERROR illegal borrow
+    let xc = x_coord(p); //~ ERROR cannot root
     assert!(*xc == 3);
     return xc;
 }
diff --git a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
index bf8f227b573..d23e20130f7 100644
--- a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
+++ b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
@@ -17,7 +17,7 @@ fn foo(cond: &fn() -> bool, box: &fn() -> @int) {
 
 	// Here we complain because the resulting region
 	// of this borrow is the fn body as a whole.
-        y = borrow(x); //~ ERROR illegal borrow: cannot root managed value long enough
+        y = borrow(x); //~ ERROR cannot root
 
         assert!(*x == *y);
         if cond() { break; }
diff --git a/src/test/compile-fail/regions-nested-fns-2.rs b/src/test/compile-fail/regions-nested-fns-2.rs
index 2e9a4eb1410..fe995052c52 100644
--- a/src/test/compile-fail/regions-nested-fns-2.rs
+++ b/src/test/compile-fail/regions-nested-fns-2.rs
@@ -13,7 +13,7 @@ fn ignore(_f: &fn<'z>(&'z int) -> &'z int) {}
 fn nested() {
     let y = 3;
     ignore(|z| {
-        if false { &y } else { z } //~ ERROR illegal borrow
+        if false { &y } else { z } //~ ERROR borrowed value does not live long enough
     });
 }
 
diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs
index 3089c362a50..74399967446 100644
--- a/src/test/compile-fail/regions-nested-fns.rs
+++ b/src/test/compile-fail/regions-nested-fns.rs
@@ -16,7 +16,7 @@ fn nested<'x>(x: &'x int) {
 
     ignore::<&fn<'z>(&'z int)>(|z| {
         ay = x;
-        ay = &y;  //~ ERROR cannot infer an appropriate lifetime
+        ay = &y;
         ay = z;
     });
 
diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs
index f916b0d95c2..f600f6f6edd 100644
--- a/src/test/compile-fail/regions-ret-borrowed-1.rs
+++ b/src/test/compile-fail/regions-ret-borrowed-1.rs
@@ -19,6 +19,7 @@ fn with<'a, R>(f: &fn(x: &'a int) -> R) -> R {
 fn return_it<'a>() -> &'a int {
     with(|o| o) //~ ERROR mismatched types
         //~^ ERROR reference is not valid outside of its lifetime
+        //~^^ ERROR reference is not valid outside of its lifetime
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs
index 157b99de9e8..a3540f8f931 100644
--- a/src/test/compile-fail/regions-ret-borrowed.rs
+++ b/src/test/compile-fail/regions-ret-borrowed.rs
@@ -22,6 +22,7 @@ fn with<R>(f: &fn(x: &int) -> R) -> R {
 fn return_it() -> &int {
     with(|o| o) //~ ERROR mismatched types
         //~^ ERROR reference is not valid outside of its lifetime
+        //~^^ ERROR reference is not valid outside of its lifetime
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-ret.rs b/src/test/compile-fail/regions-ret.rs
index be7b28f6ef4..f1a7bdf2281 100644
--- a/src/test/compile-fail/regions-ret.rs
+++ b/src/test/compile-fail/regions-ret.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn f<'a>(_x : &'a int) -> &'a int {
-    return &3; //~ ERROR illegal borrow
+    return &3; //~ ERROR borrowed value does not live long enough
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-var-type-out-of-scope.rs b/src/test/compile-fail/regions-var-type-out-of-scope.rs
index 7d75ac74349..addf20fd702 100644
--- a/src/test/compile-fail/regions-var-type-out-of-scope.rs
+++ b/src/test/compile-fail/regions-var-type-out-of-scope.rs
@@ -14,7 +14,7 @@ fn foo(cond: bool) {
     let mut x;
 
     if cond {
-        x = &3; //~ ERROR illegal borrow: borrowed value does not live long enough
+        x = &3; //~ ERROR borrowed value does not live long enough
         assert!((*x == 3));
     }
 }
diff --git a/src/test/compile-fail/swap-no-lval.rs b/src/test/compile-fail/swap-no-lval.rs
index 4fe30792e4b..eca5fb0d315 100644
--- a/src/test/compile-fail/swap-no-lval.rs
+++ b/src/test/compile-fail/swap-no-lval.rs
@@ -10,6 +10,6 @@
 
 fn main() {
     5 <-> 3;
-    //~^ ERROR swapping to and from non-lvalue
-    //~^^ ERROR swapping to and from non-lvalue
+    //~^ ERROR cannot assign
+    //~^^ ERROR cannot assign
 }
diff --git a/src/test/compile-fail/writing-to-immutable-vec.rs b/src/test/compile-fail/writing-to-immutable-vec.rs
index 3f4c8ccef81..faa3d6cfe47 100644
--- a/src/test/compile-fail/writing-to-immutable-vec.rs
+++ b/src/test/compile-fail/writing-to-immutable-vec.rs
@@ -8,5 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to immutable vec content
-fn main() { let v: ~[int] = ~[1, 2, 3]; v[1] = 4; }
+fn main() {
+    let v: ~[int] = ~[1, 2, 3];
+    v[1] = 4; //~ ERROR cannot assign
+}
diff --git a/src/test/compile-fail/issue-4500.rs b/src/test/run-pass/borrowck-nested-calls.rs
index 356a6449821..4494f5f2fa3 100644
--- a/src/test/compile-fail/issue-4500.rs
+++ b/src/test/run-pass/borrowck-nested-calls.rs
@@ -8,7 +8,25 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-fn main () {
-    let mut _p: & int = & 4;
-    _p = &*~3; //~ ERROR illegal borrow
+// xfail-test #5074 nested method calls
+
+// Test that (safe) nested calls with `&mut` receivers are permitted.
+
+struct Foo {a: uint, b: uint}
+
+pub impl Foo {
+    fn inc_a(&mut self, v: uint) { self.a += v; }
+
+    fn next_b(&mut self) -> uint {
+        let b = self.b;
+        self.b += 1;
+        b
+    }
+}
+
+fn main() {
+    let mut f = Foo {a: 22, b: 23};
+    f.inc_a(f.next_b());
+    assert_eq!(f.a, 22+23);
+    assert_eq!(f.b, 24);
 }
diff --git a/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs b/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs
index 0e67532d7a1..b0d06dae10d 100644
--- a/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs
+++ b/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs
@@ -1,10 +1,10 @@
 trait Reverser {
-    fn reverse(&self);
+    fn reverse(self);
 }
 
 impl<'self> Reverser for &'self mut [uint] {
-    fn reverse(&self) {
-        vec::reverse(*self);
+    fn reverse(self) {
+        vec::reverse(self);
     }
 }
 
diff --git a/src/test/run-pass/issue-2735-2.rs b/src/test/run-pass/issue-2735-2.rs
index 96f76b0fd6b..ca584e1a6e3 100644
--- a/src/test/run-pass/issue-2735-2.rs
+++ b/src/test/run-pass/issue-2735-2.rs
@@ -9,27 +9,25 @@
 // except according to those terms.
 
 // This test should behave exactly like issue-2735-3
-struct defer<'self> {
-    b: &'self mut bool,
+struct defer {
+    b: @mut bool,
 }
 
 #[unsafe_destructor]
-impl<'self> Drop for defer<'self> {
+impl Drop for defer {
     fn finalize(&self) {
-        unsafe {
-            *(self.b) = true;
-        }
+        *self.b = true;
     }
 }
 
-fn defer<'r>(b: &'r mut bool) -> defer<'r> {
+fn defer(b: @mut bool) -> defer {
     defer {
         b: b
     }
 }
 
 pub fn main() {
-    let mut dtor_ran = false;
-    let _  = defer(&mut dtor_ran);
-    assert!((dtor_ran));
+    let dtor_ran = @mut false;
+    let _  = defer(dtor_ran);
+    assert!(*dtor_ran);
 }
diff --git a/src/test/run-pass/issue-2735-3.rs b/src/test/run-pass/issue-2735-3.rs
index 50e3c946f50..44ca5d6929b 100644
--- a/src/test/run-pass/issue-2735-3.rs
+++ b/src/test/run-pass/issue-2735-3.rs
@@ -9,27 +9,25 @@
 // except according to those terms.
 
 // This test should behave exactly like issue-2735-2
-struct defer<'self> {
-    b: &'self mut bool,
+struct defer {
+    b: @mut bool,
 }
 
 #[unsafe_destructor]
-impl<'self> Drop for defer<'self> {
+impl Drop for defer {
     fn finalize(&self) {
-        unsafe {
-            *(self.b) = true;
-        }
+        *self.b = true;
     }
 }
 
-fn defer<'r>(b: &'r mut bool) -> defer<'r> {
+fn defer(b: @mut bool) -> defer {
     defer {
         b: b
     }
 }
 
 pub fn main() {
-    let mut dtor_ran = false;
-    defer(&mut dtor_ran);
-    assert!((dtor_ran));
+    let dtor_ran = @mut false;
+    defer(dtor_ran);
+    assert!(*dtor_ran);
 }