about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/hashmap.rs8
-rw-r--r--src/libcore/private.rs28
-rw-r--r--src/libcore/str.rs8
-rw-r--r--src/libcore/vec.rs17
-rw-r--r--src/librustc/middle/borrowck/check_loans.rs62
-rw-r--r--src/librustc/middle/borrowck/gather_loans.rs68
-rw-r--r--src/librustc/middle/borrowck/loan.rs192
-rw-r--r--src/librustc/middle/borrowck/mod.rs47
-rw-r--r--src/librustc/middle/borrowck/preserve.rs15
-rw-r--r--src/librustc/middle/mem_categorization.rs195
-rw-r--r--src/librustc/middle/trans/reflect.rs168
-rw-r--r--src/librustc/middle/ty.rs3
-rw-r--r--src/librustc/middle/typeck/infer/lattice.rs9
-rw-r--r--src/librustc/middle/typeck/infer/region_inference.rs112
-rw-r--r--src/librustc/middle/typeck/infer/unify.rs90
-rw-r--r--src/libstd/arc.rs85
-rw-r--r--src/libstd/priority_queue.rs6
-rw-r--r--src/libstd/smallintmap.rs9
-rw-r--r--src/libstd/treemap.rs56
-rw-r--r--src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs124
-rw-r--r--src/test/compile-fail/borrowck-borrow-from-stack-variable.rs124
-rw-r--r--src/test/compile-fail/borrowck-confuse-region.rs26
-rw-r--r--src/test/compile-fail/borrowck-imm-field-mut-base.rs30
-rw-r--r--src/test/compile-fail/borrowck-insert-during-each.rs (renamed from src/test/compile-fail/borrowck-imm-field-imm-base.rs)27
-rw-r--r--src/test/compile-fail/borrowck-reborrow-from-mut.rs106
25 files changed, 1043 insertions, 572 deletions
diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs
index 70358bab468..f8793f7e2ae 100644
--- a/src/libcore/hashmap.rs
+++ b/src/libcore/hashmap.rs
@@ -383,7 +383,9 @@ pub mod linear {
                 },
             };
 
-            self.value_for_bucket(idx)
+            unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker
+                ::cast::transmute_region(self.value_for_bucket(idx))
+            }
         }
 
         /// Return the value corresponding to the key in the map, or create,
@@ -412,7 +414,9 @@ pub mod linear {
                 },
             };
 
-            self.value_for_bucket(idx)
+            unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker
+                ::cast::transmute_region(self.value_for_bucket(idx))
+            }
         }
 
         fn consume(&mut self, f: fn(K, V)) {
diff --git a/src/libcore/private.rs b/src/libcore/private.rs
index 6864572adff..038f61350b2 100644
--- a/src/libcore/private.rs
+++ b/src/libcore/private.rs
@@ -256,15 +256,15 @@ pub unsafe fn shared_mutable_state<T: Owned>(data: T) ->
 }
 
 #[inline(always)]
-pub unsafe fn get_shared_mutable_state<T: Owned>(rc: &a/SharedMutableState<T>)
-        -> &a/mut T {
+pub unsafe fn get_shared_mutable_state<T: Owned>(
+    rc: *SharedMutableState<T>) -> *mut T
+{
     unsafe {
         let ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data);
         assert ptr.count > 0;
-        // Cast us back into the correct region
-        let r = cast::transmute_region(option::get_ref(&ptr.data));
+        let r = cast::transmute(option::get_ref(&ptr.data));
         cast::forget(move ptr);
-        return cast::transmute_mut(r);
+        return r;
     }
 }
 #[inline(always)]
@@ -376,15 +376,17 @@ impl<T: Owned> Exclusive<T> {
     // the exclusive. Supporting that is a work in progress.
     #[inline(always)]
     unsafe fn with<U>(f: fn(x: &mut T) -> U) -> U {
-        let rec = unsafe { get_shared_mutable_state(&self.x) };
-        do rec.lock.lock {
-            if rec.failed {
-                die!(~"Poisoned exclusive - another task failed inside!");
+        unsafe {
+            let rec = get_shared_mutable_state(&self.x);
+            do (*rec).lock.lock {
+                if (*rec).failed {
+                    die!(~"Poisoned exclusive - another task failed inside!");
+                }
+                (*rec).failed = true;
+                let result = f(&mut (*rec).data);
+                (*rec).failed = false;
+                move result
             }
-            rec.failed = true;
-            let result = f(&mut rec.data);
-            rec.failed = false;
-            move result
         }
     }
 
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 47e68401485..6665ab6c6f7 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -2071,17 +2071,19 @@ pub mod raw {
 
     /// Appends a byte to a string. (Not UTF-8 safe).
     pub unsafe fn push_byte(s: &mut ~str, b: u8) {
-        reserve_at_least(&mut *s, s.len() + 1);
+        let new_len = s.len() + 1;
+        reserve_at_least(&mut *s, new_len);
         do as_buf(*s) |buf, len| {
             let buf: *mut u8 = ::cast::reinterpret_cast(&buf);
             *ptr::mut_offset(buf, len) = b;
         }
-        set_len(&mut *s, s.len() + 1);
+        set_len(&mut *s, new_len);
     }
 
     /// Appends a vector of bytes to a string. (Not UTF-8 safe).
     unsafe fn push_bytes(s: &mut ~str, bytes: &[u8]) {
-        reserve_at_least(&mut *s, s.len() + bytes.len());
+        let new_len = s.len() + bytes.len();
+        reserve_at_least(&mut *s, new_len);
         for vec::each(bytes) |byte| { push_byte(&mut *s, *byte); }
     }
 
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 9ad5d9f32da..16cad87c060 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -623,13 +623,15 @@ unsafe fn push_fast<T>(v: &mut ~[T], initval: T) {
 
 #[inline(never)]
 fn push_slow<T>(v: &mut ~[T], initval: T) {
-    reserve_at_least(&mut *v, v.len() + 1u);
+    let new_len = v.len() + 1;
+    reserve_at_least(&mut *v, new_len);
     unsafe { push_fast(v, initval) }
 }
 
 #[inline(always)]
 pub fn push_all<T: Copy>(v: &mut ~[T], rhs: &[const T]) {
-    reserve(&mut *v, v.len() + rhs.len());
+    let new_len = v.len() + rhs.len();
+    reserve(&mut *v, new_len);
 
     for uint::range(0u, rhs.len()) |i| {
         push(&mut *v, unsafe { raw::get(rhs, i) })
@@ -638,7 +640,8 @@ pub fn push_all<T: Copy>(v: &mut ~[T], rhs: &[const T]) {
 
 #[inline(always)]
 pub fn push_all_move<T>(v: &mut ~[T], mut rhs: ~[T]) {
-    reserve(&mut *v, v.len() + rhs.len());
+    let new_len = v.len() + rhs.len();
+    reserve(&mut *v, new_len);
     unsafe {
         do as_mut_buf(rhs) |p, len| {
             for uint::range(0, len) |i| {
@@ -663,9 +666,9 @@ pub fn truncate<T>(v: &mut ~[T], newlen: uint) {
                 let mut dropped = rusti::init();
                 dropped <-> *ptr::mut_offset(p, i);
             }
-            raw::set_len(&mut *v, newlen);
         }
     }
+    unsafe { raw::set_len(&mut *v, newlen); }
 }
 
 /**
@@ -740,7 +743,8 @@ pub pure fn append_mut<T: Copy>(lhs: ~[mut T], rhs: &[const T]) -> ~[mut T] {
  * * initval - The value for the new elements
  */
 pub fn grow<T: Copy>(v: &mut ~[T], n: uint, initval: &T) {
-    reserve_at_least(&mut *v, v.len() + n);
+    let new_len = v.len() + n;
+    reserve_at_least(&mut *v, new_len);
     let mut i: uint = 0u;
 
     while i < n {
@@ -763,7 +767,8 @@ pub fn grow<T: Copy>(v: &mut ~[T], n: uint, initval: &T) {
  *             value
  */
 pub fn grow_fn<T>(v: &mut ~[T], n: uint, op: iter::InitOp<T>) {
-    reserve_at_least(&mut *v, v.len() + n);
+    let new_len = v.len() + n;
+    reserve_at_least(&mut *v, new_len);
     let mut i: uint = 0u;
     while i < n {
         v.push(op(i));
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index 251fec68486..b6b94faa3db 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -305,22 +305,31 @@ impl CheckLoanCtxt {
             return;
         }
 
-        match (old_loan.mutbl, new_loan.mutbl) {
-            (m_const, _) | (_, m_const) | (m_imm, m_imm) => {
-                /*ok*/
+        match (old_loan.kind, new_loan.kind) {
+            (PartialFreeze, PartialTake) | (PartialTake, PartialFreeze) |
+            (TotalFreeze, PartialFreeze) | (PartialFreeze, TotalFreeze) |
+            (Immobile, _) | (_, Immobile) |
+            (PartialFreeze, PartialFreeze) |
+            (PartialTake, PartialTake) |
+            (TotalFreeze, TotalFreeze) => {
+                /* ok */
             }
 
-            (m_mutbl, m_mutbl) | (m_mutbl, m_imm) | (m_imm, m_mutbl) => {
+            (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.mut_to_str(new_loan.mutbl)));
+                         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.mut_to_str(old_loan.mutbl)));
+                         self.bccx.loan_kind_to_str(old_loan.kind)));
             }
         }
     }
@@ -348,13 +357,13 @@ impl CheckLoanCtxt {
             // are only assigned once
         } else {
             match cmt.mutbl {
-              m_mutbl => { /*ok*/ }
-              m_const | m_imm => {
-                self.bccx.span_err(
-                    ex.span,
-                    at.ing_form(self.bccx.cmt_to_str(cmt)));
-                return;
-              }
+                McDeclared | McInherited => { /*ok*/ }
+                McReadOnly | McImmutable => {
+                    self.bccx.span_err(
+                        ex.span,
+                        at.ing_form(self.bccx.cmt_to_str(cmt)));
+                    return;
+                }
             }
         }
 
@@ -428,19 +437,20 @@ impl CheckLoanCtxt {
                                                   cmt: cmt,
                                                   lp: @loan_path) {
         for self.walk_loans_of(ex.id, lp) |loan| {
-            match loan.mutbl {
-              m_const => { /*ok*/ }
-              m_mutbl | m_imm => {
-                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;
-              }
+            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;
+                }
             }
         }
 
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
index d4c45828858..65518398eb7 100644
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ b/src/librustc/middle/borrowck/gather_loans.rs
@@ -20,7 +20,10 @@ use core::prelude::*;
 
 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::{req_maps};
+use middle::borrowck::loan;
 use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant};
 use middle::mem_categorization::{mem_categorization_ctxt};
 use middle::mem_categorization::{opt_deref_kind};
@@ -340,13 +343,22 @@ impl GatherLoanCtxt {
     fn guarantee_valid(@mut self,
                        cmt: cmt,
                        req_mutbl: ast::mutability,
-                       scope_r: ty::Region) {
+                       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=%s, scope_r=%s)",
+        debug!("guarantee_valid(cmt=%s, req_mutbl=%?, \
+                loan_kind=%?, scope_r=%s)",
                self.bccx.cmt_to_repr(cmt),
-               self.bccx.mut_to_str(req_mutbl),
+               req_mutbl,
+               loan_kind,
                region_to_str(self.tcx(), scope_r));
         let _i = indenter();
 
@@ -362,10 +374,10 @@ impl GatherLoanCtxt {
           // it within that scope, the loan will be detected and an
           // error will be reported.
           Some(_) => {
-              match self.bccx.loan(cmt, scope_r, req_mutbl) {
+              match loan::loan(self.bccx, cmt, scope_r, loan_kind) {
                   Err(ref e) => { self.bccx.report((*e)); }
                   Ok(move loans) => {
-                      self.add_loans(cmt, req_mutbl, scope_r, move loans);
+                      self.add_loans(cmt, loan_kind, scope_r, move loans);
                   }
               }
           }
@@ -378,7 +390,7 @@ impl GatherLoanCtxt {
           // pointer is desired, that is ok as long as we are pure)
           None => {
             let result: bckres<PreserveCondition> = {
-                do self.check_mutbl(req_mutbl, cmt).chain |pc1| {
+                do self.check_mutbl(loan_kind, cmt).chain |pc1| {
                     do self.bccx.preserve(cmt, scope_r,
                                           self.item_ub,
                                           self.root_ub).chain |pc2| {
@@ -446,37 +458,41 @@ impl GatherLoanCtxt {
     // reqires an immutable pointer, but `f` lives in (aliased)
     // mutable memory.
     fn check_mutbl(@mut self,
-                   req_mutbl: ast::mutability,
+                   loan_kind: LoanKind,
                    cmt: cmt)
                 -> bckres<PreserveCondition> {
-        debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)",
-               req_mutbl, cmt.mutbl);
+        debug!("check_mutbl(loan_kind=%?, cmt.mutbl=%?)",
+               loan_kind, cmt.mutbl);
 
-        if req_mutbl == m_const || req_mutbl == cmt.mutbl {
-            debug!("required is const or they are the same");
-            Ok(PcOk)
-        } else {
-            let e = bckerr { cmt: cmt, code: err_mutbl(req_mutbl) };
-            if req_mutbl == m_imm {
-                // if this is an @mut box, then it's generally OK to borrow as
-                // &imm; this will result in a write guard
-                if cmt.cat.is_mutable_box() {
+        match loan_kind {
+            Immobile => Ok(PcOk),
+
+            TotalTake | PartialTake => {
+                if cmt.mutbl.is_mutable() {
                     Ok(PcOk)
                 } else {
-                    // you can treat mutable things as imm if you are pure
-                    debug!("imm required, must be pure");
+                    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))
                 }
-            } else {
-                Err(e)
             }
         }
     }
 
     fn add_loans(@mut self,
                  cmt: cmt,
-                 req_mutbl: ast::mutability,
+                 loan_kind: LoanKind,
                  scope_r: ty::Region,
                  +loans: ~[Loan]) {
         if loans.len() == 0 {
@@ -526,7 +542,7 @@ impl GatherLoanCtxt {
 
         self.add_loans_to_scope_id(scope_id, move loans);
 
-        if req_mutbl == m_imm && cmt.mutbl != m_imm {
+        if loan_kind.is_freeze() && !cmt.mutbl.is_immutable() {
             self.bccx.stats.loaned_paths_imm += 1;
 
             if self.tcx().sess.borrowck_note_loan() {
@@ -542,7 +558,9 @@ impl GatherLoanCtxt {
     fn add_loans_to_scope_id(@mut self,
                              scope_id: ast::node_id,
                              +loans: ~[Loan]) {
-        debug!("adding %u loans to scope_id %?", loans.len(), scope_id);
+        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);
diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs
index da22b20fd1d..3b6d735ae78 100644
--- a/src/librustc/middle/borrowck/loan.rs
+++ b/src/librustc/middle/borrowck/loan.rs
@@ -44,6 +44,8 @@ FIXME #4730 --- much more needed, don't have time to write this all up now
 use core::prelude::*;
 
 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};
@@ -57,27 +59,26 @@ use core::result::{Err, Ok, Result};
 use syntax::ast::{m_const, m_imm, m_mutbl};
 use syntax::ast;
 
-impl BorrowckCtxt {
-    fn loan(&self,
+pub fn loan(bccx: @BorrowckCtxt,
             cmt: cmt,
             scope_region: ty::Region,
-            mutbl: ast::mutability) -> bckres<~[Loan]> {
-        let mut lc = LoanContext {
-            bccx: self,
-            scope_region: scope_region,
-            loans: ~[]
-        };
-        match lc.loan(cmt, mutbl, true) {
-            Err(ref e) => return Err((*e)),
-            Ok(()) => {}
-        }
-        // XXX: Workaround for borrow check bug.
-        Ok(copy lc.loans)
+            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(()) => {}
     }
+    // XXX: Workaround for borrow check bug.
+    Ok(copy lc.loans)
 }
 
 struct LoanContext {
-    bccx: &BorrowckCtxt,
+    bccx: @BorrowckCtxt,
 
     // the region scope for which we must preserve the memory
     scope_region: ty::Region,
@@ -87,12 +88,13 @@ struct LoanContext {
 }
 
 impl LoanContext {
-    fn tcx(&mut self) -> ty::ctxt { self.bccx.tcx }
+    fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
 
     fn loan(&mut self,
             cmt: cmt,
-            req_mutbl: ast::mutability,
-            owns_lent_data: bool) -> bckres<()> {
+            loan_kind: LoanKind,
+            owns_lent_data: bool) -> bckres<()>
+    {
         /*!
          *
          * The main routine.
@@ -107,9 +109,9 @@ impl LoanContext {
          *                     discussion in `issue_loan()`.
          */
 
-        debug!("loan(%s, %s)",
+        debug!("loan(%s, %?)",
                self.bccx.cmt_to_repr(cmt),
-               self.bccx.mut_to_str(req_mutbl));
+               loan_kind);
         let _i = indenter();
 
         // see stable() above; should only be called when `cmt` is lendable
@@ -127,15 +129,16 @@ impl LoanContext {
                 ~"rvalue with a non-none lp");
           }
           cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => {
-            let local_scope_id = self.tcx().region_map.get(&local_id);
-            self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl,
+              // FIXME(#4903)
+            let local_scope_id = self.bccx.tcx.region_map.get(&local_id);
+            self.issue_loan(cmt, ty::re_scope(local_scope_id), loan_kind,
                             owns_lent_data)
           }
           cat_stack_upvar(cmt) => {
-            self.loan(cmt, req_mutbl, owns_lent_data)
+            self.loan(cmt, loan_kind, owns_lent_data)
           }
           cat_discr(base, _) => {
-            self.loan(base, req_mutbl, owns_lent_data)
+            self.loan(base, loan_kind, owns_lent_data)
           }
           cat_comp(cmt_base, comp_field(_, m)) |
           cat_comp(cmt_base, comp_index(_, m)) => {
@@ -145,13 +148,13 @@ impl LoanContext {
             // 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, req_mutbl, m,
+            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, req_mutbl, m_imm,
+            self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm,
                                   owns_lent_data)
           }
           cat_comp(cmt_base, comp_variant(enum_did)) => {
@@ -159,10 +162,10 @@ impl LoanContext {
             // 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, req_mutbl, m_imm,
+                self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm,
                                       owns_lent_data)
             } else {
-                self.loan_unstable_deref(cmt, cmt_base, req_mutbl,
+                self.loan_unstable_deref(cmt, cmt_base, loan_kind,
                                          owns_lent_data)
             }
           }
@@ -170,7 +173,7 @@ impl LoanContext {
             // 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, req_mutbl,
+            self.loan_unstable_deref(cmt, cmt_base, loan_kind,
                                      owns_lent_data)
           }
           cat_deref(cmt_base, _, region_ptr(ast::m_mutbl, region)) => {
@@ -178,8 +181,8 @@ impl LoanContext {
             // 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, m_imm, false).chain |_| {
-                self.issue_loan(cmt, region, m_const, owns_lent_data)
+            do self.loan(cmt_base, TotalFreeze, false).chain |_| {
+                self.issue_loan(cmt, region, loan_kind, owns_lent_data)
             }
           }
           cat_deref(_, _, unsafe_ptr) |
@@ -199,66 +202,38 @@ impl LoanContext {
     fn loan_stable_comp(&mut self,
                         cmt: cmt,
                         cmt_base: cmt,
-                        req_mutbl: ast::mutability,
+                        loan_kind: LoanKind,
                         comp_mutbl: ast::mutability,
-                        owns_lent_data: bool) -> bckres<()> {
-        // Determine the mutability that the base component must have,
-        // given the required mutability of the pointer (`req_mutbl`)
-        // and the declared mutability of the component (`comp_mutbl`).
-        // This is surprisingly subtle.
-        //
-        // Note that the *declared* mutability of the component is not
-        // necessarily the same as cmt.mutbl, since a component
-        // declared as immutable but embedded in a mutable context
-        // becomes mutable.  It's best to think of comp_mutbl as being
-        // either MUTABLE or DEFAULT, not MUTABLE or IMMUTABLE.  We
-        // should really patch up the AST to reflect this distinction.
-        //
-        // Let's consider the cases below:
-        //
-        // 1. mut required, mut declared: In this case, the base
-        //    component must merely be const.  The reason is that it
-        //    does not matter if the base component is borrowed as
-        //    mutable or immutable, as the mutability of the base
-        //    component is overridden in the field declaration itself
-        //    (see `compile-fail/borrowck-mut-field-imm-base.rs`)
-        //
-        // 2. mut required, imm declared: This would only be legal if
-        //    the component is embeded in a mutable context.  However,
-        //    we detect mismatches between the mutability of the value
-        //    as a whole and the required mutability in `issue_loan()`
-        //    above.  In any case, presuming that the component IS
-        //    embedded in a mutable context, both the component and
-        //    the base must be loaned as MUTABLE.  This is to ensure
-        //    that there is no loan of the base as IMMUTABLE, which
-        //    would imply that the component must be IMMUTABLE too
-        //    (see `compile-fail/borrowck-imm-field-imm-base.rs`).
-        //
-        // 3. mut required, const declared: this shouldn't really be
-        //    possible, since I don't think you can declare a const
-        //    field, but I guess if we DID permit such a declaration
-        //    it would be equivalent to the case above?
-        //
-        // 4. imm required, * declared: In this case, the base must be
-        //    immutable.  This is true regardless of what was declared
-        //    for this subcomponent, this if the base is mutable, the
-        //    subcomponent must be mutable.
-        //    (see `compile-fail/borrowck-imm-field-mut-base.rs`).
-        //
-        // 5. const required, * declared: In this case, the base need
-        //    only be const, since we don't ultimately care whether
-        //    the subcomponent is mutable or not.
-        let base_mutbl = match (req_mutbl, comp_mutbl) {
-            (m_mutbl, m_mutbl) => m_const, // (1)
-            (m_mutbl, _) => m_mutbl,       // (2, 3)
-            (m_imm, _) => m_imm,           // (4)
-            (m_const, _) => m_const        // (5)
+                        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_mutbl, owns_lent_data).chain |_ok| {
+        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, req_mutbl,
+            self.issue_loan(cmt, ty::re_static, loan_kind,
                             owns_lent_data)
         }
     }
@@ -269,23 +244,23 @@ impl LoanContext {
     fn loan_unstable_deref(&mut self,
                            cmt: cmt,
                            cmt_base: cmt,
-                           req_mutbl: ast::mutability,
+                           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, m_imm, owns_lent_data).chain |_| {
+        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, req_mutbl,
+            self.issue_loan(cmt, ty::re_static, loan_kind,
                             owns_lent_data)
         }
     }
 
     fn issue_loan(&mut self,
-                  cmt: cmt,
-                  scope_ub: ty::Region,
-                  req_mutbl: ast::mutability,
-                  owns_lent_data: bool) -> bckres<()> {
+                  +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
@@ -297,25 +272,15 @@ impl LoanContext {
         // reborrowed.
 
         if !owns_lent_data ||
-            self.bccx.is_subregion_of(/*bad*/copy self.scope_region,
-                                      scope_ub) {
-            match req_mutbl {
-                m_mutbl => {
-                    // We do not allow non-mutable data to be loaned
-                    // out as mutable under any circumstances.
-                    if cmt.mutbl != m_mutbl {
-                        return Err(bckerr {
-                            cmt:cmt,
-                            code:err_mutbl(req_mutbl)
-                        });
-                    }
-                }
-                m_const | m_imm => {
-                    // However, mutable data can be loaned out as
-                    // immutable (and any data as const).  The
-                    // `check_loans` pass will then guarantee that no
-                    // writes occur for the duration of the loan.
-                }
+            self.bccx.is_subregion_of(self.scope_region, scope_ub)
+        {
+            if loan_kind.is_take() && !cmt.mutbl.is_mutable() {
+                // 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 {
@@ -323,8 +288,9 @@ impl LoanContext {
                 // loan process does not apply at all.
                 lp: cmt.lp.get(),
                 cmt: cmt,
-                mutbl: req_mutbl
+                kind: loan_kind
             });
+
             return Ok(());
         } else {
             // The loan being requested lives longer than the data
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index 3d45ee461dc..dd8f889a057 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -368,7 +368,7 @@ pub enum bckerr_code {
     err_mut_uniq,
     err_mut_variant,
     err_root_not_permitted,
-    err_mutbl(ast::mutability),
+    err_mutbl(LoanKind),
     err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
     err_out_of_scope(ty::Region, ty::Region) // superscope, subscope
 }
@@ -390,8 +390,19 @@ pub enum MoveError {
 // shorthand for something that fails with `bckerr` or succeeds with `T`
 pub type bckres<T> = Result<T, bckerr>;
 
+#[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)
+}
+
 /// a complete record of a loan that was granted
-pub struct Loan {lp: @loan_path, cmt: cmt, mutbl: ast::mutability}
+pub struct Loan {lp: @loan_path,
+                 cmt: cmt,
+                 kind: LoanKind}
 
 /// maps computed by `gather_loans` that are then used by `check_loans`
 ///
@@ -420,6 +431,22 @@ pub fn save_and_restore_managed<T:Copy,U>(save_and_restore_t: @mut T,
     move u
 }
 
+impl LoanKind {
+    fn is_freeze(&self) -> bool {
+        match *self {
+            TotalFreeze | PartialFreeze => true,
+            _ => false
+        }
+    }
+
+    fn is_take(&self) -> bool {
+        match *self {
+            TotalTake | PartialTake => true,
+            _ => false
+        }
+    }
+}
+
 /// Creates and returns a new root_map
 
 pub impl root_map_key : to_bytes::IterBytes {
@@ -520,9 +547,9 @@ pub impl BorrowckCtxt {
 
     fn bckerr_to_str(&self, err: bckerr) -> ~str {
         match err.code {
-            err_mutbl(req) => {
+            err_mutbl(lk) => {
                 fmt!("creating %s alias to %s",
-                     self.mut_to_str(req),
+                     self.loan_kind_to_str(lk),
                      self.cmt_to_str(err.cmt))
             }
             err_mut_uniq => {
@@ -599,9 +626,17 @@ pub impl BorrowckCtxt {
         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 loan_to_repr(&self, loan: &Loan) -> ~str {
-        fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)",
-             loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl)
+        fmt!("Loan(lp=%?, cmt=%s, kind=%?)",
+             loan.lp, self.cmt_to_repr(loan.cmt), loan.kind)
     }
 }
 
diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs
index 097f0579362..a123793e20b 100644
--- a/src/librustc/middle/borrowck/preserve.rs
+++ b/src/librustc/middle/borrowck/preserve.rs
@@ -190,10 +190,10 @@ impl PreserveCtxt {
             // 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 = %?", self.bccx.mut_to_str(base.mutbl));
+            debug!("base.mutbl = %?", base.mutbl);
             if cmt.cat.derefs_through_mutable_box() {
                 self.attempt_root(cmt, base, derefs)
-            } else if base.mutbl == m_imm {
+            } else if base.mutbl.is_immutable() {
                 let non_rooting_ctxt = PreserveCtxt {
                     root_managed_data: false,
                     ..*self
@@ -293,14 +293,11 @@ impl PreserveCtxt {
           // the base is preserved, but if we are not mutable then
           // purity is required
           Ok(PcOk) => {
-            match cmt_base.mutbl {
-              m_mutbl | m_const => {
-                Ok(PcIfPure(bckerr {cmt:cmt, code:code}))
+              if !cmt_base.mutbl.is_immutable() {
+                  Ok(PcIfPure(bckerr {cmt:cmt, code:code}))
+              } else {
+                  Ok(PcOk)
               }
-              m_imm => {
-                Ok(PcOk)
-              }
-            }
           }
 
           // the base requires purity too, that's fine
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index fd9271af6c6..c4612e76024 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -108,6 +108,14 @@ pub enum special_kind {
     sk_heap_upvar
 }
 
+#[deriving_eq]
+pub enum MutabilityCategory {
+    McImmutable, // Immutable.
+    McReadOnly,  // Read-only (`const`)
+    McDeclared,  // Directly declared as mutable.
+    McInherited  // Inherited from the fact that owner is mutable.
+}
+
 // a complete categorization of a value indicating where it originated
 // and how it is located, as well as the mutability of the memory in
 // which the value is stored.
@@ -115,12 +123,12 @@ pub enum special_kind {
 // note: cmt stands for "categorized mutable type".
 #[deriving_eq]
 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: ast::mutability,  // mutability of expr as lvalue
-    ty: ty::t                // type of the expr
+    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_;
@@ -298,8 +306,55 @@ pub struct mem_categorization_ctxt {
     method_map: typeck::method_map,
 }
 
-pub impl &mem_categorization_ctxt {
-    fn cat_expr(expr: @ast::expr) -> cmt {
+impl ToStr for MutabilityCategory {
+    pure fn to_str(&self) -> ~str {
+        fmt!("%?", *self)
+    }
+}
+
+impl MutabilityCategory {
+    static fn from_mutbl(m: ast::mutability) -> MutabilityCategory {
+        match m {
+            m_imm => McImmutable,
+            m_const => McReadOnly,
+            m_mutbl => McDeclared
+        }
+    }
+
+    fn inherit(&self) -> MutabilityCategory {
+        match *self {
+            McImmutable => McImmutable,
+            McReadOnly => McReadOnly,
+            McDeclared => McInherited,
+            McInherited => McInherited
+        }
+    }
+
+    fn is_mutable(&self) -> bool {
+        match *self {
+            McImmutable | McReadOnly => false,
+            McDeclared | McInherited => true
+        }
+    }
+
+    fn is_immutable(&self) -> bool {
+        match *self {
+            McImmutable => true,
+            McReadOnly | McDeclared | McInherited => false
+        }
+    }
+
+    fn to_user_str(&self) -> ~str {
+        match *self {
+            McDeclared | McInherited => ~"mutable",
+            McImmutable => ~"immutable",
+            McReadOnly => ~"const"
+        }
+    }
+}
+
+pub impl mem_categorization_ctxt {
+    fn cat_expr(&self, expr: @ast::expr) -> cmt {
         match self.tcx.adjustments.find(&expr.id) {
             None => {
                 // No adjustments.
@@ -323,7 +378,8 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn cat_expr_autoderefd(expr: @ast::expr,
+    fn cat_expr_autoderefd(&self,
+                           expr: @ast::expr,
                            adjustment: &ty::AutoAdjustment) -> cmt {
         let mut cmt = self.cat_expr_unadjusted(expr);
         for uint::range(1, adjustment.autoderefs+1) |deref| {
@@ -332,7 +388,7 @@ pub impl &mem_categorization_ctxt {
         return cmt;
     }
 
-    fn cat_expr_unadjusted(expr: @ast::expr) -> cmt {
+    fn cat_expr_unadjusted(&self, expr: @ast::expr) -> cmt {
         debug!("cat_expr: id=%d expr=%s",
                expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr()));
 
@@ -392,7 +448,8 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn cat_def(id: ast::node_id,
+    fn cat_def(&self,
+               id: ast::node_id,
                span: span,
                expr_ty: ty::t,
                def: ast::def) -> cmt {
@@ -409,7 +466,7 @@ pub impl &mem_categorization_ctxt {
                 span:span,
                 cat:cat_special(sk_static_item),
                 lp:None,
-                mutbl:m_imm,
+                mutbl: McImmutable,
                 ty:expr_ty
             }
           }
@@ -420,7 +477,7 @@ pub impl &mem_categorization_ctxt {
 
             // m: mutability of the argument
             // lp: loan path, must be none for aliasable things
-            let m = if mutbl {m_mutbl} else {m_imm};
+            let m = if mutbl {McDeclared} else {McImmutable};
             let lp = match ty::resolved_mode(self.tcx, mode) {
                 ast::by_copy => Some(@lp_arg(vid)),
                 ast::by_ref => None,
@@ -438,7 +495,7 @@ pub impl &mem_categorization_ctxt {
                 span:span,
                 cat:cat_arg(vid),
                 lp:lp,
-                mutbl:m,
+                mutbl: m,
                 ty:expr_ty
             }
           }
@@ -458,7 +515,7 @@ pub impl &mem_categorization_ctxt {
                 span:span,
                 cat:cat,
                 lp:loan_path,
-                mutbl:m_imm,
+                mutbl: McImmutable,
                 ty:expr_ty
             }
           }
@@ -485,7 +542,7 @@ pub impl &mem_categorization_ctxt {
                         span:span,
                         cat:cat_special(sk_heap_upvar),
                         lp:None,
-                        mutbl:m_imm,
+                        mutbl:McImmutable,
                         ty:expr_ty
                     }
                 }
@@ -493,7 +550,7 @@ pub impl &mem_categorization_ctxt {
           }
 
           ast::def_local(vid, mutbl) => {
-            let m = if mutbl {m_mutbl} else {m_imm};
+            let m = if mutbl {McDeclared} else {McImmutable};
             @cmt_ {
                 id:id,
                 span:span,
@@ -511,14 +568,15 @@ pub impl &mem_categorization_ctxt {
                 span:span,
                 cat:cat_local(vid),
                 lp:Some(@lp_local(vid)),
-                mutbl:m_imm,
+                mutbl:McImmutable,
                 ty:expr_ty
             }
           }
         }
     }
 
-    fn cat_variant<N: ast_node>(arg: N,
+    fn cat_variant<N: ast_node>(&self,
+                                arg: N,
                                 enum_did: ast::def_id,
                                 cmt: cmt) -> cmt {
         @cmt_ {
@@ -526,18 +584,18 @@ pub impl &mem_categorization_ctxt {
             span: arg.span(),
             cat: cat_comp(cmt, comp_variant(enum_did)),
             lp: cmt.lp.map(|l| @lp_comp(*l, comp_variant(enum_did)) ),
-            mutbl: cmt.mutbl, // imm iff in an immutable context
+            mutbl: cmt.mutbl.inherit(),
             ty: self.tcx.ty(arg)
         }
     }
 
-    fn cat_rvalue<N: ast_node>(elt: N, expr_ty: ty::t) -> cmt {
+    fn cat_rvalue<N: ast_node>(&self, elt: N, expr_ty: ty::t) -> cmt {
         @cmt_ {
             id:elt.id(),
             span:elt.span(),
             cat:cat_rvalue,
             lp:None,
-            mutbl:m_imm,
+            mutbl:McImmutable,
             ty:expr_ty
         }
     }
@@ -546,17 +604,21 @@ pub impl &mem_categorization_ctxt {
     /// component is inherited from the base it is a part of. For
     /// example, a record field is mutable if it is declared mutable
     /// or if the container is mutable.
-    fn inherited_mutability(base_m: ast::mutability,
-                          comp_m: ast::mutability) -> ast::mutability {
+    fn inherited_mutability(&self,
+                            base_m: MutabilityCategory,
+                            comp_m: ast::mutability) -> MutabilityCategory
+    {
         match comp_m {
-          m_imm => {base_m}  // imm: as mutable as the container
-          m_mutbl | m_const => {comp_m}
+            m_imm => base_m.inherit(),
+            m_const => McReadOnly,
+            m_mutbl => McDeclared
         }
     }
 
     /// The `field_id` parameter is the ID of the enclosing expression or
     /// pattern. It is used to determine which variant of an enum is in use.
-    fn cat_field<N:ast_node>(node: N,
+    fn cat_field<N:ast_node>(&self,
+                             node: N,
                              base_cmt: cmt,
                              f_name: ast::ident,
                              field_id: ast::node_id) -> cmt {
@@ -584,7 +646,8 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn cat_deref_fn<N:ast_node>(node: N,
+    fn cat_deref_fn<N:ast_node>(&self,
+                                node: N,
                                 base_cmt: cmt,
                                 deref_cnt: uint) -> cmt
     {
@@ -594,11 +657,13 @@ pub impl &mem_categorization_ctxt {
         // know what type lies at the other end, so we just call it
         // `()` (the empty tuple).
 
-        let mt = ty::mt {ty: ty::mk_tup(self.tcx, ~[]), mutbl: m_imm};
+        let mt = ty::mt {ty: ty::mk_tup(self.tcx, ~[]),
+                         mutbl: m_imm};
         return self.cat_deref_common(node, base_cmt, deref_cnt, mt);
     }
 
-    fn cat_deref<N:ast_node>(node: N,
+    fn cat_deref<N:ast_node>(&self,
+                             node: N,
                              base_cmt: cmt,
                              deref_cnt: uint) -> cmt
     {
@@ -615,7 +680,8 @@ pub impl &mem_categorization_ctxt {
         return self.cat_deref_common(node, base_cmt, deref_cnt, mt);
     }
 
-    fn cat_deref_common<N:ast_node>(node: N,
+    fn cat_deref_common<N:ast_node>(&self,
+                                    node: N,
                                     base_cmt: cmt,
                                     deref_cnt: uint,
                                     mt: ty::mt) -> cmt
@@ -644,7 +710,7 @@ pub impl &mem_categorization_ctxt {
                         self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
                     }
                     gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => {
-                        mt.mutbl
+                        MutabilityCategory::from_mutbl(mt.mutbl)
                     }
                 };
 
@@ -673,7 +739,9 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn cat_index<N: ast_node>(elt: N, base_cmt: cmt) -> cmt {
+    fn cat_index<N: ast_node>(&self,
+                              elt: N,
+                              base_cmt: cmt) -> cmt {
         let mt = match ty::index(self.tcx, base_cmt.ty) {
           Some(mt) => mt,
           None => {
@@ -700,7 +768,7 @@ pub impl &mem_categorization_ctxt {
                 self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
               }
               gc_ptr(_) | region_ptr(_, _) | unsafe_ptr => {
-                mt.mutbl
+                MutabilityCategory::from_mutbl(mt.mutbl)
               }
             };
 
@@ -714,21 +782,21 @@ pub impl &mem_categorization_ctxt {
                 ty:mt.ty
             };
 
-            comp(elt, deref_cmt, base_cmt.ty, m, mt.ty)
+            comp(elt, deref_cmt, base_cmt.ty, m, mt)
           }
 
           deref_comp(_) => {
             // 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.ty)
+            comp(elt, base_cmt, base_cmt.ty, m, mt)
           }
         };
 
         fn comp<N: ast_node>(elt: N, of_cmt: cmt,
-                             vect: ty::t, mutbl: ast::mutability,
-                             ty: ty::t) -> cmt
+                             vect: ty::t, mutbl: MutabilityCategory,
+                             mt: ty::mt) -> cmt
         {
-            let comp = comp_index(vect, mutbl);
+            let comp = comp_index(vect, mt.mutbl);
             let index_lp = of_cmt.lp.map(|lp| @lp_comp(*lp, comp) );
             @cmt_ {
                 id:elt.id(),
@@ -736,46 +804,55 @@ pub impl &mem_categorization_ctxt {
                 cat:cat_comp(of_cmt, comp),
                 lp:index_lp,
                 mutbl:mutbl,
-                ty:ty
+                ty:mt.ty
             }
         }
     }
 
-    fn cat_tuple_elt<N: ast_node>(elt: N, cmt: cmt) -> cmt {
+    fn cat_tuple_elt<N: ast_node>(&self,
+                                  elt: N,
+                                  cmt: cmt) -> cmt {
         @cmt_ {
             id: elt.id(),
             span: elt.span(),
             cat: cat_comp(cmt, comp_tuple),
             lp: cmt.lp.map(|l| @lp_comp(*l, comp_tuple) ),
-            mutbl: cmt.mutbl, // imm iff in an immutable context
+            mutbl: cmt.mutbl.inherit(),
             ty: self.tcx.ty(elt)
         }
     }
 
-    fn cat_anon_struct_field<N: ast_node>(elt: N, cmt: cmt) -> cmt {
+    fn cat_anon_struct_field<N: ast_node>(&self,
+                                          elt: N,
+                                          cmt: cmt) -> cmt {
         @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)),
-            mutbl: cmt.mutbl, // imm iff in an immutable context
+            mutbl: cmt.mutbl.inherit(),
             ty: self.tcx.ty(elt)
         }
     }
 
-    fn cat_method_ref(expr: @ast::expr, expr_ty: ty::t) -> cmt {
+    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:m_imm,
+            mutbl:McImmutable,
             ty:expr_ty
         }
     }
 
-    fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
-
+    fn cat_pattern(&self,
+                   cmt: cmt,
+                   pat: @ast::pat,
+                   op: fn(cmt, @ast::pat))
+    {
         // Here, `cmt` is the categorization for the value being
         // matched and pat is the pattern it is being matched against.
         //
@@ -901,7 +978,7 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn cat_to_repr(cat: categorization) -> ~str {
+    fn cat_to_repr(&self, cat: categorization) -> ~str {
         match cat {
           cat_special(sk_method) => ~"method",
           cat_special(sk_static_item) => ~"static_item",
@@ -924,7 +1001,7 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn mut_to_str(mutbl: ast::mutability) -> ~str {
+    fn mut_to_str(&self, mutbl: ast::mutability) -> ~str {
         match mutbl {
           m_mutbl => ~"mutable",
           m_const => ~"const",
@@ -932,7 +1009,7 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn ptr_sigil(ptr: ptr_kind) -> ~str {
+    fn ptr_sigil(&self, ptr: ptr_kind) -> ~str {
         match ptr {
           uniq_ptr => ~"~",
           gc_ptr(_) => ~"@",
@@ -941,7 +1018,7 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn comp_to_repr(comp: comp_kind) -> ~str {
+    fn comp_to_repr(&self, comp: comp_kind) -> ~str {
         match comp {
           comp_field(fld, _) => self.tcx.sess.str_of(fld),
           comp_index(*) => ~"[]",
@@ -951,7 +1028,7 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn lp_to_str(lp: @loan_path) -> ~str {
+    fn lp_to_str(&self, lp: @loan_path) -> ~str {
         match *lp {
           lp_local(node_id) => {
             fmt!("local(%d)", node_id)
@@ -971,17 +1048,17 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn cmt_to_repr(cmt: cmt) -> ~str {
-        fmt!("{%s id:%d m:%s lp:%s ty:%s}",
+    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,
-             self.mut_to_str(cmt.mutbl),
+             cmt.mutbl,
              cmt.lp.map_default(~"none", |p| self.lp_to_str(*p) ),
              ty_to_str(self.tcx, cmt.ty))
     }
 
-    fn cmt_to_str(cmt: cmt) -> ~str {
-        let mut_str = self.mut_to_str(cmt.mutbl);
+    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",
@@ -1016,7 +1093,7 @@ pub impl &mem_categorization_ctxt {
         }
     }
 
-    fn region_to_str(r: ty::Region) -> ~str {
+    fn region_to_str(&self, r: ty::Region) -> ~str {
         region_to_str(self.tcx, r)
     }
 }
diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs
index 16677530ecd..83fcc175837 100644
--- a/src/librustc/middle/trans/reflect.rs
+++ b/src/librustc/middle/trans/reflect.rs
@@ -116,26 +116,26 @@ pub impl Reflector {
     fn bracketed(&mut self,
                  bracket_name: ~str,
                  +extra: ~[ValueRef],
-                 inner: &fn()) {
+                 inner: &fn(&mut Reflector)) {
         // XXX: Bad copy.
         self.visit(~"enter_" + bracket_name, copy extra);
-        inner();
+        inner(self);
         self.visit(~"leave_" + bracket_name, extra);
     }
 
     fn vstore_name_and_extra(&mut self,
                              t: ty::t,
-                             vstore: ty::vstore,
-                             f: fn(+s: ~str,+v: ~[ValueRef])) {
+                             vstore: ty::vstore) -> (~str, ~[ValueRef])
+    {
         match vstore {
-          ty::vstore_fixed(n) => {
-            let extra = vec::append(~[self.c_uint(n)],
-                                    self.c_size_and_align(t));
-            f(~"fixed", extra)
-          }
-          ty::vstore_slice(_) => f(~"slice", ~[]),
-          ty::vstore_uniq => f(~"uniq", ~[]),
-          ty::vstore_box => f(~"box", ~[])
+            ty::vstore_fixed(n) => {
+                let extra = vec::append(~[self.c_uint(n)],
+                                        self.c_size_and_align(t));
+                (~"fixed", extra)
+            }
+            ty::vstore_slice(_) => (~"slice", ~[]),
+            ty::vstore_uniq => (~"uniq", ~[]),
+            ty::vstore_box => (~"box", ~[])
         }
     }
 
@@ -168,47 +168,60 @@ pub impl Reflector {
           ty::ty_float(ast::ty_f32) => self.leaf(~"f32"),
           ty::ty_float(ast::ty_f64) => self.leaf(~"f64"),
 
-          ty::ty_unboxed_vec(mt) => self.visit(~"vec", self.c_mt(mt)),
+          ty::ty_unboxed_vec(mt) => {
+              let values = self.c_mt(mt);
+              self.visit(~"vec", values)
+          }
+
           ty::ty_estr(vst) => {
-            do self.vstore_name_and_extra(t, vst) |name, extra| {
-                self.visit(~"estr_" + name, extra)
-            }
+              let (name, extra) = self.vstore_name_and_extra(t, vst);
+              self.visit(~"estr_" + name, extra)
           }
           ty::ty_evec(mt, vst) => {
-            do self.vstore_name_and_extra(t, vst) |name, extra| {
-                self.visit(~"evec_" + name, extra +
-                           self.c_mt(mt))
-            }
+              let (name, extra) = self.vstore_name_and_extra(t, vst);
+              let extra = extra + self.c_mt(mt);
+              self.visit(~"evec_" + name, extra)
+          }
+          ty::ty_box(mt) => {
+              let extra = self.c_mt(mt);
+              self.visit(~"box", extra)
+          }
+          ty::ty_uniq(mt) => {
+              let extra = self.c_mt(mt);
+              self.visit(~"uniq", extra)
+          }
+          ty::ty_ptr(mt) => {
+              let extra = self.c_mt(mt);
+              self.visit(~"ptr", extra)
+          }
+          ty::ty_rptr(_, mt) => {
+              let extra = self.c_mt(mt);
+              self.visit(~"rptr", extra)
           }
-          ty::ty_box(mt) => self.visit(~"box", self.c_mt(mt)),
-          ty::ty_uniq(mt) => self.visit(~"uniq", self.c_mt(mt)),
-          ty::ty_ptr(mt) => self.visit(~"ptr", self.c_mt(mt)),
-          ty::ty_rptr(_, mt) => self.visit(~"rptr", self.c_mt(mt)),
 
           ty::ty_rec(fields) => {
-            do self.bracketed(~"rec",
-                              ~[self.c_uint(vec::len(fields))]
-                              + self.c_size_and_align(t)) {
+              let extra = ~[self.c_uint(vec::len(fields))]
+                  + self.c_size_and_align(t);
+              do self.bracketed(~"rec", extra) |this| {
                 for fields.eachi |i, field| {
-                    self.visit(~"rec_field",
-                               ~[self.c_uint(i),
-                                 self.c_slice(
-                                     bcx.ccx().sess.str_of(field.ident))]
-                               + self.c_mt(field.mt));
+                    let extra = ~[this.c_uint(i),
+                                  this.c_slice(
+                                      bcx.ccx().sess.str_of(field.ident))]
+                        + this.c_mt(field.mt);
+                    this.visit(~"rec_field", extra);
                 }
             }
           }
 
           ty::ty_tup(tys) => {
-            do self.bracketed(~"tup",
-                              ~[self.c_uint(vec::len(tys))]
-                              + self.c_size_and_align(t)) {
-                for tys.eachi |i, t| {
-                    self.visit(~"tup_field",
-                               ~[self.c_uint(i),
-                                 self.c_tydesc(*t)]);
-                }
-            }
+              let extra = ~[self.c_uint(vec::len(tys))]
+                  + self.c_size_and_align(t);
+              do self.bracketed(~"tup", extra) |this| {
+                  for tys.eachi |i, t| {
+                      let extra = ~[this.c_uint(i), this.c_tydesc(*t)];
+                      this.visit(~"tup_field", extra);
+                  }
+              }
           }
 
           // FIXME (#2594): fetch constants out of intrinsic
@@ -242,20 +255,21 @@ pub impl Reflector {
           }
 
           ty::ty_struct(did, ref substs) => {
-            let bcx = self.bcx;
-            let tcx = bcx.ccx().tcx;
-            let fields = ty::struct_fields(tcx, did, substs);
+              let bcx = self.bcx;
+              let tcx = bcx.ccx().tcx;
+              let fields = ty::struct_fields(tcx, did, substs);
 
-            do self.bracketed(~"class", ~[self.c_uint(fields.len())]
-                              + self.c_size_and_align(t)) {
-                for fields.eachi |i, field| {
-                    self.visit(~"class_field",
-                               ~[self.c_uint(i),
-                                 self.c_slice(
-                                     bcx.ccx().sess.str_of(field.ident))]
-                               + self.c_mt(field.mt));
-                }
-            }
+              let extra = ~[self.c_uint(fields.len())]
+                  + self.c_size_and_align(t);
+              do self.bracketed(~"class", extra) |this| {
+                  for fields.eachi |i, field| {
+                      let extra = ~[this.c_uint(i),
+                                    this.c_slice(
+                                        bcx.ccx().sess.str_of(field.ident))]
+                          + this.c_mt(field.mt);
+                      this.visit(~"class_field", extra);
+                  }
+              }
           }
 
           // FIXME (#2595): visiting all the variants in turn is probably
@@ -267,20 +281,20 @@ pub impl Reflector {
             let tcx = bcx.ccx().tcx;
             let variants = ty::substd_enum_variants(tcx, did, substs);
 
-            do self.bracketed(~"enum",
-                              ~[self.c_uint(vec::len(variants))]
-                              + self.c_size_and_align(t)) {
+            let extra = ~[self.c_uint(vec::len(variants))]
+                + self.c_size_and_align(t);
+            do self.bracketed(~"enum", extra) |this| {
                 for variants.eachi |i, v| {
-                    do self.bracketed(~"enum_variant",
-                                      ~[self.c_uint(i),
-                                        self.c_int(v.disr_val),
-                                        self.c_uint(vec::len(v.args)),
-                                        self.c_slice(
-                                            bcx.ccx().sess.str_of(v.name))]) {
+                    let extra1 = ~[this.c_uint(i),
+                                   this.c_int(v.disr_val),
+                                   this.c_uint(vec::len(v.args)),
+                                   this.c_slice(
+                                       bcx.ccx().sess.str_of(v.name))];
+                    do this.bracketed(~"enum_variant", extra1) |this| {
                         for v.args.eachi |j, a| {
-                            self.visit(~"enum_variant_field",
-                                       ~[self.c_uint(j),
-                                         self.c_tydesc(*a)]);
+                            let extra = ~[this.c_uint(j),
+                                          this.c_tydesc(*a)];
+                            this.visit(~"enum_variant_field", extra);
                         }
                     }
                 }
@@ -291,13 +305,17 @@ pub impl Reflector {
           ty::ty_trait(_, _, _) => self.leaf(~"trait"),
           ty::ty_infer(_) => self.leaf(~"infer"),
           ty::ty_err => self.leaf(~"err"),
-          ty::ty_param(p) => self.visit(~"param", ~[self.c_uint(p.idx)]),
+          ty::ty_param(p) => {
+              let extra = ~[self.c_uint(p.idx)];
+              self.visit(~"param", extra)
+          }
           ty::ty_self => self.leaf(~"self"),
           ty::ty_type => self.leaf(~"type"),
           ty::ty_opaque_box => self.leaf(~"opaque_box"),
           ty::ty_opaque_closure_ptr(ck) => {
-            let ckval = ast_sigil_constant(ck);
-            self.visit(~"closure_ptr", ~[self.c_uint(ckval)])
+              let ckval = ast_sigil_constant(ck);
+              let extra = ~[self.c_uint(ckval)];
+              self.visit(~"closure_ptr", extra)
           }
         }
     }
@@ -312,14 +330,14 @@ pub impl Reflector {
                     ast::by_copy => 5u
                 }
             };
-            self.visit(~"fn_input",
-                       ~[self.c_uint(i),
+            let extra = ~[self.c_uint(i),
                          self.c_uint(modeval),
-                         self.c_tydesc(arg.ty)]);
+                         self.c_tydesc(arg.ty)];
+            self.visit(~"fn_input", extra);
         }
-        self.visit(~"fn_output",
-                   ~[self.c_uint(retval),
-                     self.c_tydesc(sig.output)]);
+        let extra = ~[self.c_uint(retval),
+                      self.c_tydesc(sig.output)];
+        self.visit(~"fn_output", extra);
     }
 }
 
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 4350c62af6f..d60360f5546 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -1934,7 +1934,6 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
         }
         cache.insert(ty_id, TC_NONE);
 
-        debug!("computing contents of %s", ty_to_str(cx, ty));
         let _i = indenter();
 
         let mut result = match get(ty).sty {
@@ -2085,8 +2084,6 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
             result = result + TC_BIG;
         }
 
-        debug!("result = %s", result.to_str());
-
         cache.insert(ty_id, result);
         return result;
     }
diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs
index 4bf8a0bae86..709864c0d13 100644
--- a/src/librustc/middle/typeck/infer/lattice.rs
+++ b/src/librustc/middle/typeck/infer/lattice.rs
@@ -118,11 +118,10 @@ pub impl CombineFields {
         // A remains a subtype of B.  Actually, there are other options,
         // but that's the route we choose to take.
 
-        self.infcx.unify(&node_a, &node_b, |new_root, new_rank| {
-            self.set_var_to_merged_bounds(new_root,
-                                          &a_bounds, &b_bounds,
-                                          new_rank)
-        })
+        let (new_root, new_rank) = self.infcx.unify(&node_a, &node_b);
+        self.set_var_to_merged_bounds(new_root,
+                                      &a_bounds, &b_bounds,
+                                      new_rank)
     }
 
     /// make variable a subtype of T
diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs
index 027b99cc421..230bfe693e0 100644
--- a/src/librustc/middle/typeck/infer/region_inference.rs
+++ b/src/librustc/middle/typeck/infer/region_inference.rs
@@ -832,7 +832,7 @@ pub impl RegionVarBindings {
           (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => {
             self.combine_vars(
                 self.lubs, a, b, span,
-                |old_r, new_r| self.make_subregion(span, old_r, new_r))
+                |this, old_r, new_r| this.make_subregion(span, old_r, new_r))
           }
 
           _ => {
@@ -859,7 +859,7 @@ pub impl RegionVarBindings {
           (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => {
             self.combine_vars(
                 self.glbs, a, b, span,
-                |old_r, new_r| self.make_subregion(span, new_r, old_r))
+                |this, old_r, new_r| this.make_subregion(span, new_r, old_r))
           }
 
           _ => {
@@ -915,7 +915,9 @@ pub impl RegionVarBindings {
                     a: Region,
                     b: Region,
                     span: span,
-                    relate: &fn(old_r: Region, new_r: Region) -> cres<()>)
+                    relate: &fn(self: &mut RegionVarBindings,
+                                old_r: Region,
+                                new_r: Region) -> cres<()>)
                  -> cres<Region> {
         let vars = TwoRegions { a: a, b: b };
         match combines.find(&vars) {
@@ -926,8 +928,8 @@ pub impl RegionVarBindings {
             if self.in_snapshot() {
                 self.undo_log.push(AddCombination(combines, vars));
             }
-            do relate(a, re_infer(ReVar(c))).then {
-                do relate(b, re_infer(ReVar(c))).then {
+            do relate(self, a, re_infer(ReVar(c))).then {
+                do relate(self, b, re_infer(ReVar(c))).then {
                     debug!("combine_vars() c=%?", c);
                     Ok(re_infer(ReVar(c)))
                 }
@@ -1035,7 +1037,8 @@ pub impl RegionVarBindings {
     */
     fn resolve_regions(&mut self) {
         debug!("RegionVarBindings: resolve_regions()");
-        self.values.put_back(self.infer_variable_values());
+        let v = self.infer_variable_values();
+        self.values.put_back(v);
     }
 }
 
@@ -1220,7 +1223,7 @@ impl RegionVarBindings {
         let mut graph = self.construct_graph();
         self.expansion(&mut graph);
         self.contraction(&mut graph);
-        self.extract_values_and_report_conflicts(&mut graph)
+        self.extract_values_and_report_conflicts(&graph)
     }
 
     fn construct_graph(&mut self) -> Graph {
@@ -1257,14 +1260,14 @@ impl RegionVarBindings {
 
         for uint::range(0, num_edges) |edge_idx| {
             match graph.edges[edge_idx].constraint {
-              ConstrainVarSubVar(copy a_id, copy b_id) => {
+              ConstrainVarSubVar(a_id, b_id) => {
                 insert_edge(&mut graph, a_id, Outgoing, edge_idx);
                 insert_edge(&mut graph, b_id, Incoming, edge_idx);
               }
-              ConstrainRegSubVar(_, copy b_id) => {
+              ConstrainRegSubVar(_, b_id) => {
                 insert_edge(&mut graph, b_id, Incoming, edge_idx);
               }
-              ConstrainVarSubReg(copy a_id, _) => {
+              ConstrainVarSubReg(a_id, _) => {
                 insert_edge(&mut graph, a_id, Outgoing, edge_idx);
               }
             }
@@ -1285,17 +1288,17 @@ impl RegionVarBindings {
     }
 
     fn expansion(&mut self, graph: &mut Graph) {
-        do self.iterate_until_fixed_point(~"Expansion", graph) |edge| {
+        do iterate_until_fixed_point(~"Expansion", graph) |nodes, edge| {
             match edge.constraint {
-              ConstrainRegSubVar(copy a_region, copy b_vid) => {
-                let b_node = &mut graph.nodes[*b_vid];
+              ConstrainRegSubVar(a_region, b_vid) => {
+                let b_node = &mut nodes[*b_vid];
                 self.expand_node(a_region, b_vid, b_node)
               }
-              ConstrainVarSubVar(copy a_vid, copy b_vid) => {
-                match graph.nodes[*a_vid].value {
+              ConstrainVarSubVar(a_vid, b_vid) => {
+                match nodes[*a_vid].value {
                   NoValue | ErrorValue => false,
-                  Value(copy a_region) => {
-                    let b_node = &mut graph.nodes[*b_vid];
+                  Value(a_region) => {
+                    let b_node = &mut nodes[*b_vid];
                     self.expand_node(a_region, b_vid, b_node)
                   }
                 }
@@ -1325,7 +1328,7 @@ impl RegionVarBindings {
             return true;
           }
 
-          Value(copy cur_region) => {
+          Value(cur_region) => {
             let lub = self.lub_concrete_regions(a_region, cur_region);
             if lub == cur_region {
                 return false;
@@ -1345,23 +1348,23 @@ impl RegionVarBindings {
     }
 
     fn contraction(&mut self, graph: &mut Graph) {
-        do self.iterate_until_fixed_point(~"Contraction", graph) |edge| {
+        do iterate_until_fixed_point(~"Contraction", graph) |nodes, edge| {
             match edge.constraint {
               ConstrainRegSubVar(*) => {
                 // This is an expansion constraint.  Ignore.
                 false
               }
-              ConstrainVarSubVar(copy a_vid, copy b_vid) => {
-                match graph.nodes[*b_vid].value {
+              ConstrainVarSubVar(a_vid, b_vid) => {
+                match nodes[*b_vid].value {
                   NoValue | ErrorValue => false,
-                  Value(copy b_region) => {
-                    let a_node = &mut graph.nodes[*a_vid];
+                  Value(b_region) => {
+                    let a_node = &mut nodes[*a_vid];
                     self.contract_node(a_vid, a_node, b_region)
                   }
                 }
               }
-              ConstrainVarSubReg(copy a_vid, copy b_region) => {
-                let a_node = &mut graph.nodes[*a_vid];
+              ConstrainVarSubReg(a_vid, b_region) => {
+                let a_node = &mut nodes[*a_vid];
                 self.contract_node(a_vid, a_node, b_region)
               }
             }
@@ -1387,7 +1390,7 @@ impl RegionVarBindings {
                 false // no change
             }
 
-            Value(copy a_region) => {
+            Value(a_region) => {
                 match a_node.classification {
                     Expanding => {
                         check_node(self, a_vid, a_node, a_region, b_region)
@@ -1438,29 +1441,10 @@ impl RegionVarBindings {
         }
     }
 
-    fn iterate_until_fixed_point(&mut self,
-                                 tag: ~str,
-                                 graph: &mut Graph,
-                                 body: &fn(edge: &GraphEdge) -> bool) {
-        let mut iteration = 0;
-        let mut changed = true;
-        let num_edges = graph.edges.len();
-        while changed {
-            changed = false;
-            iteration += 1;
-            debug!("---- %s Iteration #%u", tag, iteration);
-            for uint::range(0, num_edges) |edge_idx| {
-                changed |= body(&graph.edges[edge_idx]);
-                debug!(" >> Change after edge #%?: %?",
-                       edge_idx, graph.edges[edge_idx]);
-            }
-        }
-        debug!("---- %s Complete after %u iteration(s)", tag, iteration);
-    }
-
-    fn extract_values_and_report_conflicts(&mut self,
-                                           graph: &mut Graph)
-                                        -> ~[GraphNodeValue] {
+    fn extract_values_and_report_conflicts(
+        &mut self,
+        graph: &Graph) -> ~[GraphNodeValue]
+    {
         let dup_map = TwoRegionsMap();
         graph.nodes.mapi(|idx, node| {
             match node.value {
@@ -1525,7 +1509,7 @@ impl RegionVarBindings {
     }
 
     fn report_error_for_expanding_node(&mut self,
-                                       graph: &mut Graph,
+                                       graph: &Graph,
                                        dup_map: TwoRegionsMap,
                                        node_idx: RegionVid) {
         // Errors in expanding nodes result from a lower-bound that is
@@ -1578,7 +1562,7 @@ impl RegionVarBindings {
     }
 
     fn report_error_for_contracting_node(&mut self,
-                                         graph: &mut Graph,
+                                         graph: &Graph,
                                          dup_map: TwoRegionsMap,
                                          node_idx: RegionVid) {
         // Errors in contracting nodes result from two upper-bounds
@@ -1632,7 +1616,7 @@ impl RegionVarBindings {
     }
 
     fn collect_concrete_regions(&mut self,
-                                graph: &mut Graph,
+                                graph: &Graph,
                                 orig_node_idx: RegionVid,
                                 dir: Direction)
                              -> ~[SpannedRegion] {
@@ -1676,7 +1660,7 @@ impl RegionVarBindings {
     }
 
     fn each_edge(&mut self,
-                 graph: &mut Graph,
+                 graph: &Graph,
                  node_idx: RegionVid,
                  dir: Direction,
                  op: fn(edge: &GraphEdge) -> bool) {
@@ -1690,3 +1674,25 @@ impl RegionVarBindings {
         }
     }
 }
+
+fn iterate_until_fixed_point(
+    tag: ~str,
+    graph: &mut Graph,
+    body: &fn(nodes: &mut [GraphNode], edge: &GraphEdge) -> bool)
+{
+    let mut iteration = 0;
+    let mut changed = true;
+    let num_edges = graph.edges.len();
+    while changed {
+        changed = false;
+        iteration += 1;
+        debug!("---- %s Iteration #%u", tag, iteration);
+        for uint::range(0, num_edges) |edge_idx| {
+            changed |= body(graph.nodes, &graph.edges[edge_idx]);
+            debug!(" >> Change after edge #%?: %?",
+                   edge_idx, graph.edges[edge_idx]);
+        }
+    }
+    debug!("---- %s Complete after %u iteration(s)", tag, iteration);
+}
+
diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs
index 7668388e323..69e4f373f97 100644
--- a/src/librustc/middle/typeck/infer/unify.rs
+++ b/src/librustc/middle/typeck/infer/unify.rs
@@ -43,9 +43,10 @@ pub trait UnifyVid<T> {
 }
 
 pub impl InferCtxt {
-    fn get<T:Copy, V:Copy Eq Vid UnifyVid<T>>(&mut self,
-                                              +vid: V)
-                                           -> Node<V, T> {
+    fn get<T:Copy, V:Copy+Eq+Vid+UnifyVid<T>>(
+        &mut self,
+        +vid: V) -> Node<V, T>
+    {
         /*!
          *
          * Find the root node for `vid`. This uses the standard
@@ -53,27 +54,38 @@ pub impl InferCtxt {
          * http://en.wikipedia.org/wiki/Disjoint-set_data_structure
          */
 
+        let tcx = self.tcx;
         let vb = UnifyVid::appropriate_vals_and_bindings(self);
-        let vid_u = vid.to_uint();
-        match vb.vals.find(vid_u) {
-          None => {
-            self.tcx.sess.bug(fmt!("failed lookup of vid `%u`", vid_u));
-          }
-          Some(ref var_val) => {
-            match (*var_val) {
-              Redirect(vid) => {
-                let node: Node<V,T> = self.get(vid);
-                if node.root != vid {
-                    // Path compression
-                    vb.vals.insert(vid.to_uint(), Redirect(node.root));
+        return helper(tcx, vb, vid);
+
+        fn helper<T:Copy, V:Copy+Eq+Vid>(
+            tcx: ty::ctxt,
+            vb: &mut ValsAndBindings<V,T>,
+            vid: V) -> Node<V, T>
+        {
+            let vid_u = vid.to_uint();
+            match vb.vals.find(vid_u) {
+                None => {
+                    tcx.sess.bug(fmt!(
+                        "failed lookup of vid `%u`", vid_u));
+                }
+                Some(ref 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}
+                        }
+                    }
                 }
-                node
-              }
-              Root(ref pt, rk) => {
-                Node {root: vid, possible_types: *pt, rank: rk}
-              }
             }
-          }
         }
     }
 
@@ -86,21 +98,22 @@ pub impl InferCtxt {
          * Sets the value for `vid` to `new_v`.  `vid` MUST be a root node!
          */
 
-        let vb = UnifyVid::appropriate_vals_and_bindings(self);
-        let old_v = vb.vals.get(vid.to_uint());
-        vb.bindings.push((vid, old_v));
-        vb.vals.insert(vid.to_uint(), new_v);
+        debug!("Updating variable %s to %s",
+               vid.to_str(), new_v.inf_str(self));
 
-        debug!("Updating variable %s from %s to %s",
-               vid.to_str(), old_v.inf_str(self), new_v.inf_str(self));
+        { // 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));
+            vb.vals.insert(vid.to_uint(), new_v);
+        }
     }
 
-    fn unify<T:Copy InferStr, V:Copy Vid ToStr UnifyVid<T>, R>(
-            &mut self,
-            node_a: &Node<V, T>,
-            node_b: &Node<V, T>,
-            op: &fn(new_root: V, new_rank: uint) -> R
-    ) -> R {
+    fn unify<T:Copy InferStr, V:Copy Vid ToStr UnifyVid<T>>(
+        &mut self,
+        node_a: &Node<V, T>,
+        node_b: &Node<V, T>) -> (V, uint)
+    {
         // Rank optimization: if you don't know what it is, check
         // out <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>
 
@@ -113,17 +126,17 @@ pub impl InferCtxt {
             // a has greater rank, so a should become b's parent,
             // i.e., b should redirect to a.
             self.set(node_b.root, Redirect(node_a.root));
-            op(node_a.root, node_a.rank)
+            (node_a.root, node_a.rank)
         } else if node_a.rank < node_b.rank {
             // b has greater rank, so a should redirect to b.
             self.set(node_a.root, Redirect(node_b.root));
-            op(node_b.root, node_b.rank)
+            (node_b.root, node_b.rank)
         } else {
             // If equal, redirect one to the other and increment the
             // other's rank.
             assert node_a.rank == node_b.rank;
             self.set(node_b.root, Redirect(node_a.root));
-            op(node_a.root, node_a.rank + 1)
+            (node_a.root, node_a.rank + 1)
         }
     }
 
@@ -183,9 +196,8 @@ pub impl InferCtxt {
             }
         };
 
-        self.unify(&node_a, &node_b, |new_root, new_rank| {
-            self.set(new_root, Root(combined, new_rank));
-        });
+        let (new_root, new_rank) = self.unify(&node_a, &node_b);
+        self.set(new_root, Root(combined, new_rank));
         return uok();
     }
 
diff --git a/src/libstd/arc.rs b/src/libstd/arc.rs
index a1fd7a66f7e..ff28d2cbebf 100644
--- a/src/libstd/arc.rs
+++ b/src/libstd/arc.rs
@@ -184,25 +184,30 @@ impl<T: Owned> &MutexARC<T> {
      */
     #[inline(always)]
     unsafe fn access<U>(blk: fn(x: &mut T) -> U) -> U {
-        let state = unsafe { get_shared_mutable_state(&self.x) };
-        // Borrowck would complain about this if the function were not already
-        // unsafe. See borrow_rwlock, far below.
-        do (&state.lock).lock {
-            check_poison(true, state.failed);
-            let _z = PoisonOnFail(&mut state.failed);
-            blk(&mut state.data)
+        unsafe {
+            let state = get_shared_mutable_state(&self.x);
+            // Borrowck would complain about this if the function were
+            // not already unsafe. See borrow_rwlock, far below.
+            do (&(*state).lock).lock {
+                check_poison(true, (*state).failed);
+                let _z = PoisonOnFail(&mut (*state).failed);
+                blk(&mut (*state).data)
+            }
         }
     }
     /// As access(), but with a condvar, as sync::mutex.lock_cond().
     #[inline(always)]
     unsafe fn access_cond<U>(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U {
-        let state = unsafe { get_shared_mutable_state(&self.x) };
-        do (&state.lock).lock_cond |cond| {
-            check_poison(true, state.failed);
-            let _z = PoisonOnFail(&mut state.failed);
-            blk(&mut state.data,
-                &Condvar { is_mutex: true, failed: &mut state.failed,
-                           cond: cond })
+        unsafe {
+            let state = get_shared_mutable_state(&self.x);
+            do (&(*state).lock).lock_cond |cond| {
+                check_poison(true, (*state).failed);
+                let _z = PoisonOnFail(&mut (*state).failed);
+                blk(&mut (*state).data,
+                    &Condvar {is_mutex: true,
+                              failed: &mut (*state).failed,
+                              cond: cond })
+            }
         }
     }
 }
@@ -285,8 +290,10 @@ pub fn RWARC<T: Const Owned>(user_data: T) -> RWARC<T> {
  * Create a reader/writer ARC with the supplied data and a specified number
  * of condvars (as sync::rwlock_with_condvars).
  */
-pub fn rw_arc_with_condvars<T: Const Owned>(user_data: T,
-                                       num_condvars: uint) -> RWARC<T> {
+pub fn rw_arc_with_condvars<T: Const Owned>(
+    user_data: T,
+    num_condvars: uint) -> RWARC<T>
+{
     let data =
         RWARCInner { lock: rwlock_with_condvars(num_condvars),
                      failed: false, data: move user_data };
@@ -315,23 +322,28 @@ impl<T: Const Owned> &RWARC<T> {
      */
     #[inline(always)]
     fn write<U>(blk: fn(x: &mut T) -> U) -> U {
-        let state = unsafe { get_shared_mutable_state(&self.x) };
-        do borrow_rwlock(state).write {
-            check_poison(false, state.failed);
-            let _z = PoisonOnFail(&mut state.failed);
-            blk(&mut state.data)
+        unsafe {
+            let state = get_shared_mutable_state(&self.x);
+            do (*borrow_rwlock(state)).write {
+                check_poison(false, (*state).failed);
+                let _z = PoisonOnFail(&mut (*state).failed);
+                blk(&mut (*state).data)
+            }
         }
     }
     /// As write(), but with a condvar, as sync::rwlock.write_cond().
     #[inline(always)]
     fn write_cond<U>(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U {
-        let state = unsafe { get_shared_mutable_state(&self.x) };
-        do borrow_rwlock(state).write_cond |cond| {
-            check_poison(false, state.failed);
-            let _z = PoisonOnFail(&mut state.failed);
-            blk(&mut state.data,
-                &Condvar { is_mutex: false, failed: &mut state.failed,
-                           cond: cond })
+        unsafe {
+            let state = get_shared_mutable_state(&self.x);
+            do (*borrow_rwlock(state)).write_cond |cond| {
+                check_poison(false, (*state).failed);
+                let _z = PoisonOnFail(&mut (*state).failed);
+                blk(&mut (*state).data,
+                    &Condvar {is_mutex: false,
+                              failed: &mut (*state).failed,
+                              cond: cond})
+            }
         }
     }
     /**
@@ -369,11 +381,14 @@ impl<T: Const Owned> &RWARC<T> {
      * ~~~
      */
     fn write_downgrade<U>(blk: fn(v: RWWriteMode<T>) -> U) -> U {
-        let state = unsafe { get_shared_mutable_state(&self.x) };
-        do borrow_rwlock(state).write_downgrade |write_mode| {
-            check_poison(false, state.failed);
-            blk(RWWriteMode((&mut state.data, move write_mode,
-                              PoisonOnFail(&mut state.failed))))
+        unsafe {
+            let state = get_shared_mutable_state(&self.x);
+            do (*borrow_rwlock(state)).write_downgrade |write_mode| {
+                check_poison(false, (*state).failed);
+                blk(RWWriteMode((&mut (*state).data,
+                                 move write_mode,
+                                 PoisonOnFail(&mut (*state).failed))))
+            }
         }
     }
 
@@ -417,8 +432,8 @@ pub fn unwrap_rw_arc<T: Const Owned>(arc: RWARC<T>) -> T {
 // lock it. This wraps the unsafety, with the justification that the 'lock'
 // field is never overwritten; only 'failed' and 'data'.
 #[doc(hidden)]
-fn borrow_rwlock<T: Const Owned>(state: &r/mut RWARCInner<T>) -> &r/RWlock {
-    unsafe { cast::transmute(&mut state.lock) }
+fn borrow_rwlock<T: Const Owned>(state: *const RWARCInner<T>) -> *RWlock {
+    unsafe { cast::transmute(&const (*state).lock) }
 }
 
 // FIXME (#3154) ice with struct/&<T> prevents these from being structs.
diff --git a/src/libstd/priority_queue.rs b/src/libstd/priority_queue.rs
index 5248ab1742e..a64aa5e9687 100644
--- a/src/libstd/priority_queue.rs
+++ b/src/libstd/priority_queue.rs
@@ -81,7 +81,8 @@ impl <T: Ord> PriorityQueue<T> {
     /// Push an item onto the queue
     fn push(&mut self, item: T) {
         self.data.push(item);
-        self.siftup(0, self.len() - 1);
+        let new_len = self.len() - 1;
+        self.siftup(0, new_len);
     }
 
     /// Optimized version of a push followed by a pop
@@ -179,7 +180,8 @@ impl <T: Ord> PriorityQueue<T> {
     }
 
     priv fn siftdown(&mut self, pos: uint) {
-        self.siftdown_range(pos, self.len());
+        let len = self.len();
+        self.siftdown_range(pos, len);
     }
 }
 
diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs
index 9642dd0c3dd..2e5cd8956cd 100644
--- a/src/libstd/smallintmap.rs
+++ b/src/libstd/smallintmap.rs
@@ -134,10 +134,11 @@ pub impl<V> SmallIntMap<V> {
 pub impl<V: Copy> SmallIntMap<V> {
     fn update_with_key(&mut self, key: uint, val: V,
                        ff: fn(uint, V, V) -> V) -> bool {
-        match self.find(&key) {
-          None => self.insert(key, val),
-          Some(orig) => self.insert(key, ff(key, copy *orig, val)),
-        }
+        let new_val = match self.find(&key) {
+            None => val,
+            Some(orig) => ff(key, *orig, val)
+        };
+        self.insert(key, new_val)
     }
 
     fn update(&mut self, key: uint, newval: V, ff: fn(V, V) -> V) -> bool {
diff --git a/src/libstd/treemap.rs b/src/libstd/treemap.rs
index 26bf232adf5..2fdaeb545a2 100644
--- a/src/libstd/treemap.rs
+++ b/src/libstd/treemap.rs
@@ -673,45 +673,45 @@ fn remove<K: Ord, V>(node: &mut Option<~TreeNode<K, V>>, key: &K) -> bool {
             }
         };
 
-        if this {
-            *node = None;
-            return true;
-        }
+        if !this {
+            let left_level = save.left.map_default(0, |x| x.level);
+            let right_level = save.right.map_default(0, |x| x.level);
 
-        let left_level = save.left.map_default(0, |x| x.level);
-        let right_level = save.right.map_default(0, |x| x.level);
+            // re-balance, if necessary
+            if left_level < save.level - 1 || right_level < save.level - 1 {
+                save.level -= 1;
 
-        // re-balance, if necessary
-        if left_level < save.level - 1 || right_level < save.level - 1 {
-            save.level -= 1;
+                if right_level > save.level {
+                    do save.right.mutate |mut x| { x.level = save.level; x }
+                }
 
-            if right_level > save.level {
-                do save.right.mutate |mut x| { x.level = save.level; x }
-            }
+                skew(save);
 
-            skew(save);
+                match save.right {
+                    Some(ref mut right) => {
+                        skew(right);
+                        match right.right {
+                            Some(ref mut x) => { skew(x) },
+                            None => ()
+                        }
+                    }
+                    None => ()
+                }
 
-            match save.right {
-              Some(ref mut right) => {
-                skew(right);
-                match right.right {
-                  Some(ref mut x) => { skew(x) },
-                  None => ()
+                split(save);
+                match save.right {
+                    Some(ref mut x) => { split(x) },
+                    None => ()
                 }
-              }
-              None => ()
             }
 
-            split(save);
-            match save.right {
-              Some(ref mut x) => { split(x) },
-              None => ()
-            }
+            return removed;
         }
-
-        removed
       }
     }
+
+    *node = None;
+    return true;
 }
 
 #[cfg(test)]
diff --git a/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs
new file mode 100644
index 00000000000..47b6b4de642
--- /dev/null
+++ b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs
@@ -0,0 +1,124 @@
+// 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 Foo {
+  bar1: Bar,
+  bar2: Bar
+}
+
+struct Bar {
+  int1: int,
+  int2: int,
+}
+
+fn make_foo() -> ~Foo { die!() }
+
+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
+}
+
+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
+}
+
+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
+}
+
+fn borrow_same_field_twice_imm_imm() {
+    let mut foo = make_foo();
+    let _bar1 = &foo.bar1;
+    let _bar2 = &foo.bar1;
+}
+
+fn borrow_both_mut() {
+    let mut foo = make_foo();
+    let _bar1 = &mut foo.bar1;
+    let _bar2 = &mut foo.bar2;
+}
+
+fn borrow_both_mut_pattern() {
+    let mut foo = make_foo();
+    match *foo {
+        Foo { bar1: ref mut _bar1, bar2: ref mut _bar2 } => {}
+    }
+}
+
+fn borrow_var_and_pattern() {
+    let mut foo = make_foo();
+    let _bar1 = &mut foo.bar1;
+    match *foo {
+        Foo { bar1: ref mut _bar1, bar2: _ } => {}
+        //~^ ERROR conflicts with prior loan
+    }
+}
+
+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
+}
+
+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
+}
+
+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
+}
+
+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
+}
+
+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
+}
+
+fn borrow_imm_and_base_imm() {
+    let mut foo = make_foo();
+    let _bar1 = &foo.bar1.int1;
+    let _foo1 = &foo.bar1;
+    let _foo2 = &*foo;
+}
+
+fn borrow_mut_and_imm() {
+    let mut foo = make_foo();
+    let _bar1 = &mut foo.bar1;
+    let _foo1 = &foo.bar2;
+}
+
+fn borrow_mut_from_imm() {
+    let foo = make_foo();
+    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+}
+
+fn borrow_long_path_both_mut() {
+    let mut foo = make_foo();
+    let _bar1 = &mut foo.bar1.int1;
+    let _foo1 = &mut foo.bar2.int2;
+}
+
+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
new file mode 100644
index 00000000000..30757cc6e77
--- /dev/null
+++ b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs
@@ -0,0 +1,124 @@
+// 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 Foo {
+  bar1: Bar,
+  bar2: Bar
+}
+
+struct Bar {
+  int1: int,
+  int2: int,
+}
+
+fn make_foo() -> Foo { die!() }
+
+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
+}
+
+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
+}
+
+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
+}
+
+fn borrow_same_field_twice_imm_imm() {
+    let mut foo = make_foo();
+    let _bar1 = &foo.bar1;
+    let _bar2 = &foo.bar1;
+}
+
+fn borrow_both_mut() {
+    let mut foo = make_foo();
+    let _bar1 = &mut foo.bar1;
+    let _bar2 = &mut foo.bar2;
+}
+
+fn borrow_both_mut_pattern() {
+    let mut foo = make_foo();
+    match foo {
+        Foo { bar1: ref mut _bar1, bar2: ref mut _bar2 } => {}
+    }
+}
+
+fn borrow_var_and_pattern() {
+    let mut foo = make_foo();
+    let _bar1 = &mut foo.bar1;
+    match foo {
+        Foo { bar1: ref mut _bar1, bar2: _ } => {}
+        //~^ ERROR conflicts with prior loan
+    }
+}
+
+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
+}
+
+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
+}
+
+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
+}
+
+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
+}
+
+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
+}
+
+fn borrow_imm_and_base_imm() {
+    let mut foo = make_foo();
+    let _bar1 = &foo.bar1.int1;
+    let _foo1 = &foo.bar1;
+    let _foo2 = &foo;
+}
+
+fn borrow_mut_and_imm() {
+    let mut foo = make_foo();
+    let _bar1 = &mut foo.bar1;
+    let _foo1 = &foo.bar2;
+}
+
+fn borrow_mut_from_imm() {
+    let foo = make_foo();
+    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+}
+
+fn borrow_long_path_both_mut() {
+    let mut foo = make_foo();
+    let _bar1 = &mut foo.bar1.int1;
+    let _foo1 = &mut foo.bar2.int2;
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/borrowck-confuse-region.rs b/src/test/compile-fail/borrowck-confuse-region.rs
deleted file mode 100644
index 7b5d9829f5f..00000000000
--- a/src/test/compile-fail/borrowck-confuse-region.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.
-
-// Here we are checking that a reasonable error msg is provided.
-//
-// The current message is not ideal, but we used to say "borrowed
-// pointer has lifetime &, but the borrowed value only has lifetime &"
-// which is definitely no good.
-
-
-fn get() -> &int {
-    //~^ NOTE borrowed pointer must be valid for the anonymous lifetime #1 defined on
-    //~^^ NOTE ...but borrowed value is only valid for the block at
-    let x = 3;
-    return &x;
-    //~^ ERROR illegal borrow
-}
-
-fn main() {}
diff --git a/src/test/compile-fail/borrowck-imm-field-mut-base.rs b/src/test/compile-fail/borrowck-imm-field-mut-base.rs
deleted file mode 100644
index 5c3fe229602..00000000000
--- a/src/test/compile-fail/borrowck-imm-field-mut-base.rs
+++ /dev/null
@@ -1,30 +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 Foo {
-    mut x: uint
-}
-
-struct Bar {
-    foo: Foo
-}
-
-fn main() {
-    let mut b = Bar { foo: Foo { x: 3 } };
-    let p = &b.foo.x;
-    let q = &mut b.foo; //~ ERROR loan of mutable field as mutable conflicts with prior loan
-    //~^ ERROR loan of mutable local variable as mutable conflicts with prior loan
-    let r = &mut b; //~ ERROR loan of mutable local variable as mutable conflicts with prior loan
-    //~^ ERROR loan of mutable local variable as mutable conflicts with prior loan
-    io::println(fmt!("*p = %u", *p));
-    q.x += 1;
-    r.foo.x += 1;
-    io::println(fmt!("*p = %u", *p));
-}
diff --git a/src/test/compile-fail/borrowck-imm-field-imm-base.rs b/src/test/compile-fail/borrowck-insert-during-each.rs
index 0d850fd5cee..1dcf8268440 100644
--- a/src/test/compile-fail/borrowck-imm-field-imm-base.rs
+++ b/src/test/compile-fail/borrowck-insert-during-each.rs
@@ -8,20 +8,27 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use core::hashmap::linear::LinearSet;
+
 struct Foo {
-    x: uint
+  n: LinearSet<int>,
+}
+
+impl Foo {
+  fn foo(&mut self, fun: fn(&int)) {
+    for self.n.each |f| {
+      fun(f);
+    }
+  }
 }
 
-struct Bar {
-    foo: 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
+  }
 }
 
 fn main() {
-    let mut b = Bar { foo: Foo { x: 3 } };
-    let p = &b; //~ NOTE prior loan as immutable granted here
-    let q = &mut b.foo.x; //~ ERROR loan of mutable local variable as mutable conflicts with prior loan
-    let r = &p.foo.x;
-    io::println(fmt!("*r = %u", *r));
-    *q += 1;
-    io::println(fmt!("*r = %u", *r));
+  let mut f = Foo { n: LinearSet::new() };
+  bar(&mut f);
 }
\ No newline at end of file
diff --git a/src/test/compile-fail/borrowck-reborrow-from-mut.rs b/src/test/compile-fail/borrowck-reborrow-from-mut.rs
new file mode 100644
index 00000000000..60f817dee0c
--- /dev/null
+++ b/src/test/compile-fail/borrowck-reborrow-from-mut.rs
@@ -0,0 +1,106 @@
+// 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 Foo {
+  bar1: Bar,
+  bar2: Bar
+}
+
+struct Bar {
+  int1: int,
+  int2: int,
+}
+
+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
+}
+
+fn borrow_same_field_twice_mut_imm(foo: &mut Foo) {
+    let _bar1 = &mut foo.bar1;
+    let _bar2 = &foo.bar1;  //~ ERROR conflicts with prior loan
+}
+
+fn borrow_same_field_twice_imm_mut(foo: &mut Foo) {
+    let _bar1 = &foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+}
+
+fn borrow_same_field_twice_imm_imm(foo: &mut Foo) {
+    let _bar1 = &foo.bar1;
+    let _bar2 = &foo.bar1;
+}
+
+fn borrow_both_mut(foo: &mut Foo) {
+    let _bar1 = &mut foo.bar1;
+    let _bar2 = &mut foo.bar2;
+}
+
+fn borrow_both_mut_pattern(foo: &mut Foo) {
+    match *foo {
+        Foo { bar1: ref mut _bar1, bar2: ref mut _bar2 } => {}
+    }
+}
+
+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
+    }
+}
+
+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
+}
+
+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
+}
+
+fn borrow_mut_and_base_mut2(foo: &mut Foo) {
+    let _bar1 = &mut foo.bar1.int1;
+    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+}
+
+fn borrow_imm_and_base_mut(foo: &mut Foo) {
+    let _bar1 = &foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+}
+
+fn borrow_imm_and_base_mut2(foo: &mut Foo) {
+    let _bar1 = &foo.bar1.int1;
+    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+}
+
+fn borrow_imm_and_base_imm(foo: &mut Foo) {
+    let _bar1 = &foo.bar1.int1;
+    let _foo1 = &foo.bar1;
+    let _foo2 = &*foo;
+}
+
+fn borrow_mut_and_imm(foo: &mut Foo) {
+    let _bar1 = &mut foo.bar1;
+    let _foo1 = &foo.bar2;
+}
+
+fn borrow_mut_from_imm(foo: &Foo) {
+    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+}
+
+fn borrow_long_path_both_mut(foo: &mut Foo) {
+    let _bar1 = &mut foo.bar1.int1;
+    let _foo1 = &mut foo.bar2.int2;
+}
+
+fn main() {}