about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-07-26 08:51:57 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-07-30 14:49:28 -0700
commit5d32d03b892d67f2cc0574966b50b3eab4b8bd94 (patch)
tree60317b676fef08e531fb6e6af243222e6192f571
parent6ef13e76e95ea67103a2bc4f4becc91dc290aafc (diff)
downloadrust-5d32d03b892d67f2cc0574966b50b3eab4b8bd94.tar.gz
rust-5d32d03b892d67f2cc0574966b50b3eab4b8bd94.zip
Fix #2979: inference for lifetimes of & expressions
What we now do is to create a region variable for each &
expression (and also each borrow).  The lifetime of this
variable will be checked by borrowck to ensure it is not greater
than the lifetime of the underlying data.  This both leads to
shorter lifetimes in some cases but also longer in others,
such as taking the address to the interior of unique boxes
tht are rooted in region pointers (e.g., returning a pointer
to the interior of a sendable map).

This may lead to issue #2977 if the rvalue is not POD, because
we may drop the data in trans sooner than borrowck expects us
to.  Need to work out precisely where that fix ought to occur.
-rw-r--r--src/rustc/middle/astencode.rs26
-rw-r--r--src/rustc/middle/borrowck.rs55
-rw-r--r--src/rustc/middle/borrowck/categorization.rs14
-rw-r--r--src/rustc/middle/borrowck/gather_loans.rs219
-rw-r--r--src/rustc/middle/borrowck/loan.rs77
-rw-r--r--src/rustc/middle/borrowck/preserve.rs274
-rw-r--r--src/rustc/middle/lang_items.rs17
-rw-r--r--src/rustc/middle/region.rs59
-rw-r--r--src/rustc/middle/ty.rs6
-rw-r--r--src/rustc/middle/typeck/check.rs29
-rw-r--r--src/rustc/middle/typeck/infer.rs36
-rw-r--r--src/rustc/util/ppaux.rs7
-rw-r--r--src/test/compile-fail/borrowck-addr-of-upvar.rs14
-rw-r--r--src/test/compile-fail/borrowck-lend-flow.rs25
-rw-r--r--src/test/compile-fail/issue-511.rs3
-rw-r--r--src/test/compile-fail/kindck-owned-trait-contains.rs2
-rw-r--r--src/test/compile-fail/regions-addr-of-arg.rs2
-rw-r--r--src/test/compile-fail/regions-addr-of-self.rs4
-rw-r--r--src/test/compile-fail/regions-addr-of-upvar-self.rs6
-rw-r--r--src/test/compile-fail/regions-appearance-constraint.rs35
-rw-r--r--src/test/compile-fail/regions-creating-enums.rs4
-rw-r--r--src/test/compile-fail/regions-escape-loop-via-variable.rs5
-rw-r--r--src/test/compile-fail/regions-escape-loop-via-vec.rs8
-rw-r--r--src/test/compile-fail/regions-infer-borrow-scope-too-big.rs4
-rw-r--r--src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs19
-rw-r--r--src/test/compile-fail/regions-nested-fns-2.rs11
-rw-r--r--src/test/compile-fail/regions-nested-fns.rs1
-rw-r--r--src/test/compile-fail/regions-ret.rs6
-rw-r--r--src/test/compile-fail/regions-scoping.rs9
-rw-r--r--src/test/run-pass/borrowck-root-while-cond-2.rs4
-rw-r--r--src/test/run-pass/borrowck-root-while-cond.rs6
-rw-r--r--src/test/run-pass/regions-addr-of-interior-of-unique-box.rs13
-rw-r--r--src/test/run-pass/regions-appearance-constraint.rs22
-rw-r--r--src/test/run-pass/regions-infer-borrow-scope-addr-of.rs19
34 files changed, 719 insertions, 322 deletions
diff --git a/src/rustc/middle/astencode.rs b/src/rustc/middle/astencode.rs
index 84ada7726ab..1428d9e8982 100644
--- a/src/rustc/middle/astencode.rs
+++ b/src/rustc/middle/astencode.rs
@@ -437,21 +437,6 @@ impl of tr for method_origin {
 }
 
 // ______________________________________________________________________
-// Encoding and decoding of borrow
-
-trait read_borrow_helper {
-    fn read_borrow(xcx: extended_decode_ctxt) -> ty::borrow;
-}
-
-impl helper of read_borrow_helper for ebml::ebml_deserializer {
-    fn read_borrow(xcx: extended_decode_ctxt) -> ty::borrow {
-        let borrow = ty::deserialize_borrow(self);
-        {scope_id: xcx.tr_id(borrow.scope_id),
-         mutbl: borrow.mutbl}
-    }
-}
-
-// ______________________________________________________________________
 // Encoding and decoding vtable_res
 
 fn encode_vtable_res(ecx: @e::encode_ctxt,
@@ -766,11 +751,13 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
         }
     }
 
-    do option::iter(tcx.borrowings.find(id)) |borrow| {
+    do option::iter(tcx.borrowings.find(id)) |_borrow| {
         do ebml_w.tag(c::tag_table_borrowings) {
             ebml_w.id(id);
             do ebml_w.tag(c::tag_table_val) {
-                ty::serialize_borrow(ebml_w, borrow)
+                // N.B. We don't actually serialize borrows as, in
+                // trans, we only care whether a value is borrowed or
+                // not.
             }
         }
     }
@@ -890,7 +877,10 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
                 dcx.maps.vtable_map.insert(id,
                                            val_dsr.read_vtable_res(xcx));
             } else if tag == (c::tag_table_borrowings as uint) {
-                let borrow = val_dsr.read_borrow(xcx);
+                // N.B.: we don't actually *serialize* borrows because, in
+                // trans, the only thing we care about is whether a value was
+                // borrowed or not.
+                let borrow = {region: ty::re_static, mutbl: ast::m_imm};
                 dcx.tcx.borrowings.insert(id, borrow);
             } else {
                 xcx.dcx.tcx.sess.bug(
diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs
index 763c831265f..df09a585f47 100644
--- a/src/rustc/middle/borrowck.rs
+++ b/src/rustc/middle/borrowck.rs
@@ -321,9 +321,10 @@ type binding_map = std::map::hashmap<ast::node_id, ast::mutability>;
 enum bckerr_code {
     err_mut_uniq,
     err_mut_variant,
-    err_preserve_gc,
-    err_mutbl(ast::mutability,
-              ast::mutability)
+    err_root_not_permitted,
+    err_mutbl(ast::mutability, ast::mutability),
+    err_out_of_root_scope(ty::region, ty::region), // superscope, subscope
+    err_out_of_scope(ty::region, ty::region) // superscope, subscope
 }
 
 // Combination of an error code and the categorization of the expression
@@ -346,7 +347,7 @@ enum categorization {
 }
 
 // different kinds of pointers:
-enum ptr_kind {uniq_ptr, gc_ptr, region_ptr, unsafe_ptr}
+enum ptr_kind {uniq_ptr, gc_ptr, region_ptr(ty::region), unsafe_ptr}
 
 // I am coining the term "components" to mean "pieces of a data
 // structure accessible without a dereference":
@@ -391,10 +392,15 @@ enum loan_path {
     lp_comp(@loan_path, comp_kind)
 }
 
-// a complete record of a loan that was granted
+/// a complete record of a loan that was granted
 type loan = {lp: @loan_path, cmt: cmt, mutbl: ast::mutability};
 
-// maps computed by `gather_loans` that are then used by `check_loans`
+/// maps computed by `gather_loans` that are then used by `check_loans`
+///
+/// - `req_loan_map`: map from each block/expr to the required loans needed
+///   for the duration of that block/expr
+/// - `pure_map`: map from block/expr that must be pure to the error message
+///   that should be reported if they are not pure
 type req_maps = {
     req_loan_map: hashmap<ast::node_id, @dvec<@dvec<loan>>>,
     pure_map: hashmap<ast::node_id, bckerr>
@@ -519,7 +525,7 @@ impl to_str_methods for borrowck_ctxt {
         alt ptr {
           uniq_ptr { ~"~" }
           gc_ptr { ~"@" }
-          region_ptr { ~"&" }
+          region_ptr(_) { ~"&" }
           unsafe_ptr { ~"*" }
         }
     }
@@ -561,15 +567,6 @@ impl to_str_methods for borrowck_ctxt {
              ty_to_str(self.tcx, cmt.ty)]
     }
 
-    fn pk_to_sigil(pk: ptr_kind) -> ~str {
-        alt pk {
-          uniq_ptr {~"~"}
-          gc_ptr {~"@"}
-          region_ptr {~"&"}
-          unsafe_ptr {~"*"}
-        }
-    }
-
     fn cmt_to_str(cmt: cmt) -> ~str {
         let mut_str = self.mut_to_str(cmt.mutbl);
         alt cmt.cat {
@@ -584,7 +581,7 @@ impl to_str_methods for borrowck_ctxt {
           cat_binding(_) { ~"pattern binding" }
           cat_arg(_) { ~"argument" }
           cat_deref(_, _, pk) { #fmt["dereference of %s %s pointer",
-                                     mut_str, self.pk_to_sigil(pk)] }
+                                     mut_str, self.ptr_sigil(pk)] }
           cat_stack_upvar(_) {
             ~"captured outer " + mut_str + ~" variable in a stack closure"
           }
@@ -622,12 +619,30 @@ impl to_str_methods for borrowck_ctxt {
           err_mut_variant {
             ~"enum variant in aliasable, mutable location"
           }
-          err_preserve_gc {
-            ~"GC'd value would have to be preserved for longer \
-                 than the scope of the function"
+          err_root_not_permitted {
+            // note: I don't expect users to ever see this error
+            // message, reasons are discussed in attempt_root() in
+            // preserve.rs.
+            ~"rooting is not permitted"
+          }
+          err_out_of_root_scope(super_scope, sub_scope) {
+            #fmt["managed value would have to be rooted for lifetime %s, \
+                  but can only be rooted for lifetime %s",
+                 self.region_to_str(sub_scope),
+                 self.region_to_str(super_scope)]
+          }
+          err_out_of_scope(super_scope, sub_scope) {
+            #fmt["borrowed pointer has lifetime %s, \
+                  but the borrowed value only has lifetime %s",
+                 self.region_to_str(sub_scope),
+                 self.region_to_str(super_scope)]
           }
         }
     }
+
+    fn region_to_str(r: ty::region) -> ~str {
+        region_to_str(self.tcx, r)
+    }
 }
 
 // The inherent mutability of a component is its default mutability
diff --git a/src/rustc/middle/borrowck/categorization.rs b/src/rustc/middle/borrowck/categorization.rs
index e23d997a028..d8c88d415ac 100644
--- a/src/rustc/middle/borrowck/categorization.rs
+++ b/src/rustc/middle/borrowck/categorization.rs
@@ -50,10 +50,10 @@ fn opt_deref_kind(t: ty::t) -> option<deref_kind> {
         some(deref_ptr(uniq_ptr))
       }
 
-      ty::ty_rptr(*) |
-      ty::ty_evec(_, ty::vstore_slice(_)) |
-      ty::ty_estr(ty::vstore_slice(_)) {
-        some(deref_ptr(region_ptr))
+      ty::ty_rptr(r, _) |
+      ty::ty_evec(_, ty::vstore_slice(r)) |
+      ty::ty_estr(ty::vstore_slice(r)) {
+        some(deref_ptr(region_ptr(r)))
       }
 
       ty::ty_box(*) |
@@ -343,7 +343,7 @@ impl public_methods for borrowck_ctxt {
                     // not loanable.
                     alt ptr {
                       uniq_ptr => {some(@lp_deref(l, ptr))}
-                      gc_ptr | region_ptr | unsafe_ptr => {none}
+                      gc_ptr | region_ptr(_) | unsafe_ptr => {none}
                     }
                 };
 
@@ -353,7 +353,7 @@ impl public_methods for borrowck_ctxt {
                   uniq_ptr => {
                     self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
                   }
-                  gc_ptr | region_ptr | unsafe_ptr => {
+                  gc_ptr | region_ptr(_) | unsafe_ptr => {
                     mt.mutbl
                   }
                 };
@@ -402,7 +402,7 @@ impl public_methods for borrowck_ctxt {
               uniq_ptr => {
                 self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
               }
-              gc_ptr | region_ptr | unsafe_ptr => {
+              gc_ptr | region_ptr(_) | unsafe_ptr => {
                 mt.mutbl
               }
             };
diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs
index 3c21c6d0c0f..87e8ad7a9f2 100644
--- a/src/rustc/middle/borrowck/gather_loans.rs
+++ b/src/rustc/middle/borrowck/gather_loans.rs
@@ -8,35 +8,94 @@
 
 import categorization::{public_methods, opt_deref_kind};
 import loan::public_methods;
-import preserve::public_methods;
+import preserve::{public_methods, preserve_condition, pc_ok, pc_if_pure};
 
 export gather_loans;
 
-enum gather_loan_ctxt = @{bccx: borrowck_ctxt, req_maps: req_maps};
+/// Context used while gathering loans:
+///
+/// - `bccx`: the the borrow check context
+/// - `req_maps`: the maps computed by `gather_loans()`, see def'n of the
+///   type `req_maps` for more info
+/// - `item_ub`: the id of the block for the enclosing fn/method item
+/// - `root_ub`: the id of the outermost block for which we can root
+///   an `@T`.  This is the id of the innermost enclosing
+///   loop or function body.
+///
+/// The role of `root_ub` is to prevent us from having to accumulate
+/// vectors of rooted items at runtime.  Consider this case:
+///
+///     fn foo(...) -> int {
+///         let mut ptr: &int;
+///         while some_cond {
+///             let x: @int = ...;
+///             ptr = &*x;
+///         }
+///         *ptr
+///     }
+///
+/// If we are not careful here, we would infer the scope of the borrow `&*x`
+/// to be the body of the function `foo()` as a whole.  We would then
+/// have root each `@int` that is produced, which is an unbounded number.
+/// No good.  Instead what will happen is that `root_ub` will be set to the
+/// body of the while loop and we will refuse to root the pointer `&*x`
+/// because it would have to be rooted for a region greater than `root_ub`.
+enum gather_loan_ctxt = @{bccx: borrowck_ctxt,
+                          req_maps: req_maps,
+                          mut item_ub: ast::node_id,
+                          mut root_ub: ast::node_id};
 
 fn gather_loans(bccx: borrowck_ctxt, crate: @ast::crate) -> req_maps {
     let glcx = gather_loan_ctxt(@{bccx: bccx,
                                   req_maps: {req_loan_map: int_hash(),
-                                             pure_map: int_hash()}});
-    let v = visit::mk_vt(@{visit_expr: req_loans_in_expr
+                                             pure_map: int_hash()},
+                                  mut item_ub: 0,
+                                  mut root_ub: 0});
+    let v = visit::mk_vt(@{visit_expr: req_loans_in_expr,
+                           visit_fn: req_loans_in_fn,
                            with *visit::default_visitor()});
     visit::visit_crate(*crate, glcx, v);
     ret glcx.req_maps;
 }
 
+fn req_loans_in_fn(fk: visit::fn_kind,
+                   decl: ast::fn_decl,
+                   body: ast::blk,
+                   sp: span,
+                   id: ast::node_id,
+                   &&self: gather_loan_ctxt,
+                   v: visit::vt<gather_loan_ctxt>) {
+    // see explanation attached to the `root_ub` field:
+    let old_item_id = self.item_ub;
+    let old_root_ub = self.root_ub;
+    self.root_ub = body.node.id;
+
+    alt fk {
+      visit::fk_anon(*) | visit::fk_fn_block(*) {}
+      visit::fk_item_fn(*) | visit::fk_method(*) |
+      visit::fk_ctor(*) | visit::fk_dtor(*) {
+        self.item_ub = body.node.id;
+      }
+    }
+
+    visit::visit_fn(fk, decl, body, sp, id, self, v);
+    self.root_ub = old_root_ub;
+    self.item_ub = old_item_id;
+}
+
 fn req_loans_in_expr(ex: @ast::expr,
                      &&self: gather_loan_ctxt,
                      vt: visit::vt<gather_loan_ctxt>) {
     let bccx = self.bccx;
     let tcx = bccx.tcx;
+    let old_root_ub = self.root_ub;
 
     #debug["req_loans_in_expr(ex=%s)", pprust::expr_to_str(ex)];
 
     // If this expression is borrowed, have to ensure it remains valid:
     for tcx.borrowings.find(ex.id).each |borrow| {
         let cmt = self.bccx.cat_borrow_of_expr(ex);
-        let scope_r = ty::re_scope(borrow.scope_id);
-        self.guarantee_valid(cmt, borrow.mutbl, scope_r);
+        self.guarantee_valid(cmt, borrow.mutbl, borrow.region);
     }
 
     // Special checks for various kinds of expressions:
@@ -51,6 +110,7 @@ fn req_loans_in_expr(ex: @ast::expr,
               ty::ty_rptr(r, _) { r }
             };
         self.guarantee_valid(base_cmt, mutbl, scope_r);
+        visit::visit_expr(ex, self, vt);
       }
 
       ast::expr_call(f, args, _) {
@@ -92,7 +152,7 @@ fn req_loans_in_expr(ex: @ast::expr,
                 // fine).
                 //
                 alt opt_deref_kind(arg_ty.ty) {
-                  some(deref_ptr(region_ptr)) |
+                  some(deref_ptr(region_ptr(_))) |
                   some(deref_ptr(unsafe_ptr)) {
                     /* region pointers are (by induction) guaranteed */
                     /* unsafe pointers are the user's problem */
@@ -110,6 +170,7 @@ fn req_loans_in_expr(ex: @ast::expr,
               ast::by_move | ast::by_copy {}
             }
         }
+        visit::visit_expr(ex, self, vt);
       }
 
       ast::expr_alt(ex_v, arms, _) {
@@ -119,6 +180,7 @@ fn req_loans_in_expr(ex: @ast::expr,
                 self.gather_pat(cmt, pat, arm.body.node.id, ex.id);
             }
         }
+        visit::visit_expr(ex, self, vt);
       }
 
       ast::expr_index(rcvr, _) |
@@ -136,6 +198,7 @@ fn req_loans_in_expr(ex: @ast::expr,
         let scope_r = ty::re_scope(ex.id);
         let rcvr_cmt = self.bccx.cat_expr(rcvr);
         self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
+        visit::visit_expr(ex, self, vt);
       }
 
       ast::expr_field(rcvr, _, _)
@@ -151,13 +214,34 @@ fn req_loans_in_expr(ex: @ast::expr,
         let scope_r = ty::re_scope(self.tcx().region_map.get(ex.id));
         let rcvr_cmt = self.bccx.cat_expr(rcvr);
         self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
+        visit::visit_expr(ex, self, vt);
       }
 
-      _ { /*ok*/ }
+      // see explanation attached to the `root_ub` field:
+      ast::expr_while(cond, body) {
+        // during the condition, can only root for the condition
+        self.root_ub = cond.id;
+        vt.visit_expr(cond, self, vt);
+
+        // during body, can only root for the body
+        self.root_ub = body.node.id;
+        vt.visit_block(body, self, vt);
+      }
+
+      // see explanation attached to the `root_ub` field:
+      ast::expr_loop(body) {
+        self.root_ub = body.node.id;
+        visit::visit_expr(ex, self, vt);
+      }
+
+      _ => {
+        visit::visit_expr(ex, self, vt);
+      }
     }
 
     // Check any contained expressions:
-    visit::visit_expr(ex, self, vt);
+
+    self.root_ub = old_root_ub;
 }
 
 impl methods for gather_loan_ctxt {
@@ -192,65 +276,69 @@ impl methods for gather_loan_ctxt {
           // it within that scope, the loan will be detected and an
           // error will be reported.
           some(_) {
-            alt scope_r {
-              ty::re_scope(scope_id) {
-                let loans = self.bccx.loan(cmt, req_mutbl);
-                self.add_loans(scope_id, loans);
-
-                if req_mutbl == m_imm && cmt.mutbl != m_imm {
-                    self.bccx.loaned_paths_imm += 1;
-
-                    if self.tcx().sess.borrowck_note_loan() {
-                        self.bccx.span_note(
-                            cmt.span,
-                            #fmt["immutable loan required"]);
+            alt self.bccx.loan(cmt, scope_r, req_mutbl) {
+              err(e) => { self.bccx.report(e); }
+              ok(loans) if loans.len() == 0 => {}
+              ok(loans) => {
+                alt scope_r {
+                  ty::re_scope(scope_id) => {
+                    self.add_loans(scope_id, loans);
+
+                    if req_mutbl == m_imm && cmt.mutbl != m_imm {
+                        self.bccx.loaned_paths_imm += 1;
+
+                        if self.tcx().sess.borrowck_note_loan() {
+                            self.bccx.span_note(
+                                cmt.span,
+                                #fmt["immutable loan required"]);
+                        }
+                    } else {
+                        self.bccx.loaned_paths_same += 1;
                     }
-                } else {
-                    self.bccx.loaned_paths_same += 1;
+                  }
+                  _ => {
+                    self.bccx.tcx.sess.span_bug(
+                        cmt.span,
+                        #fmt["loans required but scope is scope_region is %s",
+                             region_to_str(self.tcx(), scope_r)]);
+                  }
                 }
               }
-              _ {
-                self.bccx.span_err(
-                    cmt.span,
-                    #fmt["cannot guarantee the stability \
-                          of this expression for the entirety of \
-                          its lifetime, %s",
-                         region_to_str(self.tcx(), scope_r)]);
-              }
             }
           }
 
-          // The path is not loanable: in that case, we must try and preserve
-          // it dynamically (or see that it is preserved by virtue of being
-          // rooted in some immutable path)
+          // The path is not loanable: in that case, we must try and
+          // preserve it dynamically (or see that it is preserved by
+          // virtue of being rooted in some immutable path).  We must
+          // also check that the mutability of the desired pointer
+          // matches with the actual mutability (but if an immutable
+          // pointer is desired, that is ok as long as we are pure)
           none {
-            let opt_scope_id = alt scope_r {
-              ty::re_scope(scope_id) { some(scope_id) }
-              _ { none }
-            };
-
-            let result = {
-                do self.check_mutbl(req_mutbl, cmt).chain |_ok| {
-                    self.bccx.preserve(cmt, opt_scope_id)
+            let result: bckres<preserve_condition> = {
+                do self.check_mutbl(req_mutbl, cmt).chain |pc1| {
+                    do self.bccx.preserve(cmt, scope_r,
+                                          self.item_ub,
+                                          self.root_ub).chain |pc2| {
+                        ok(pc1.combine(pc2))
+                    }
                 }
             };
 
             alt result {
-              ok(()) {
+              ok(pc_ok) {
                 // we were able guarantee the validity of the ptr,
                 // perhaps by rooting or because it is immutably
                 // rooted.  good.
                 self.bccx.stable_paths += 1;
               }
-              err(e) {
-                // not able to guarantee the validity of the ptr.
-                // rather than report an error, presuming that the
-                // borrow is for a limited scope, we'll make one last
-                // ditch effort and require that the scope where the
-                // borrow occurs be pure.
-                alt opt_scope_id {
-                  some(scope_id) {
-                    self.req_maps.pure_map.insert(scope_id, e);
+              ok(pc_if_pure(e)) {
+                // we are only able to guarantee the validity if
+                // the scope is pure
+                alt scope_r {
+                  ty::re_scope(pure_id) => {
+                    // if the scope is some block/expr in the fn,
+                    // then just require that this scope be pure
+                    self.req_maps.pure_map.insert(pure_id, e);
                     self.bccx.req_pure_paths += 1;
 
                     if self.tcx().sess.borrowck_note_pure() {
@@ -259,12 +347,17 @@ impl methods for gather_loan_ctxt {
                             #fmt["purity required"]);
                     }
                   }
-                  none {
-                    // otherwise, fine, I give up.
+                  _ => {
+                    // otherwise, we can't enforce purity for that
+                    // scope, so give up and report an error
                     self.bccx.report(e);
                   }
                 }
               }
+              err(e) => {
+                // we cannot guarantee the validity of this pointer
+                self.bccx.report(e);
+              }
             }
           }
         }
@@ -279,19 +372,25 @@ impl methods for gather_loan_ctxt {
     // reqires an immutable pointer, but `f` lives in (aliased)
     // mutable memory.
     fn check_mutbl(req_mutbl: ast::mutability,
-                   cmt: cmt) -> bckres<()> {
+                   cmt: cmt) -> bckres<preserve_condition> {
         alt (req_mutbl, cmt.mutbl) {
           (m_const, _) |
           (m_imm, m_imm) |
-          (m_mutbl, m_mutbl) {
-            ok(())
+          (m_mutbl, m_mutbl) => {
+            ok(pc_ok)
           }
 
           (_, m_const) |
           (m_imm, m_mutbl) |
-          (m_mutbl, m_imm) {
-            err({cmt: cmt,
-                 code: err_mutbl(req_mutbl, cmt.mutbl)})
+          (m_mutbl, m_imm) => {
+            let e = {cmt: cmt,
+                     code: err_mutbl(req_mutbl, cmt.mutbl)};
+            if req_mutbl == m_imm {
+                // you can treat mutable things as imm if you are pure
+                ok(pc_if_pure(e))
+            } else {
+                err(e)
+            }
           }
         }
     }
diff --git a/src/rustc/middle/borrowck/loan.rs b/src/rustc/middle/borrowck/loan.rs
index ee902885562..4b1726b69fa 100644
--- a/src/rustc/middle/borrowck/loan.rs
+++ b/src/rustc/middle/borrowck/loan.rs
@@ -3,17 +3,29 @@
 // of the scope S, presuming that the returned set of loans `Ls` are honored.
 
 export public_methods;
+import result::{result, ok, err};
 
 impl public_methods for borrowck_ctxt {
-    fn loan(cmt: cmt, mutbl: ast::mutability) -> @dvec<loan> {
-        let lc = loan_ctxt_(@{bccx: self, loans: @dvec()});
-        lc.loan(cmt, mutbl);
-        ret lc.loans;
+    fn loan(cmt: cmt,
+            scope_region: ty::region,
+            mutbl: ast::mutability) -> bckres<@dvec<loan>> {
+        let lc = loan_ctxt_(@{bccx: self,
+                              scope_region: scope_region,
+                              loans: @dvec()});
+        alt lc.loan(cmt, mutbl) {
+          ok(()) => {ok(lc.loans)}
+          err(e) => {err(e)}
+        }
     }
 }
 
 type loan_ctxt_ = {
     bccx: borrowck_ctxt,
+
+    // the region scope for which we must preserve the memory
+    scope_region: ty::region,
+
+    // accumulated list of loans that will be required
     loans: @dvec<loan>
 };
 
@@ -22,18 +34,30 @@ enum loan_ctxt {
 }
 
 impl loan_methods for loan_ctxt {
+    fn tcx() -> ty::ctxt { self.bccx.tcx }
+
     fn ok_with_loan_of(cmt: cmt,
-                       mutbl: ast::mutability) {
-        // Note: all cmt's that we deal with will have a non-none lp, because
-        // the entry point into this routine, `borrowck_ctxt::loan()`, rejects
-        // any cmt with a none-lp.
-        (*self.loans).push({lp:option::get(cmt.lp),
-                            cmt:cmt,
-                            mutbl:mutbl});
+                       scope_ub: ty::region,
+                       mutbl: ast::mutability) -> bckres<()> {
+        let region_map = self.tcx().region_map;
+        if region::subregion(region_map, scope_ub, self.scope_region) {
+            // Note: all cmt's that we deal with will have a non-none
+            // lp, because the entry point into this routine,
+            // `borrowck_ctxt::loan()`, rejects any cmt with a
+            // none-lp.
+            (*self.loans).push({lp: option::get(cmt.lp),
+                                cmt: cmt,
+                                mutbl: mutbl});
+            ok(())
+        } else {
+            // The loan being requested lives longer than the data
+            // being loaned out!
+            err({cmt:cmt, code:err_out_of_scope(scope_ub,
+                                                self.scope_region)})
+        }
     }
 
-    fn loan(cmt: cmt, req_mutbl: ast::mutability) {
-
+    fn loan(cmt: cmt, req_mutbl: ast::mutability) -> bckres<()> {
         #debug["loan(%s, %s)",
                self.bccx.cmt_to_repr(cmt),
                self.bccx.mut_to_str(req_mutbl)];
@@ -53,8 +77,12 @@ impl loan_methods for loan_ctxt {
                 cmt.span,
                 ~"rvalue with a non-none lp");
           }
-          cat_local(_) | cat_arg(_) | cat_stack_upvar(_) {
-            self.ok_with_loan_of(cmt, req_mutbl)
+          cat_local(local_id) | cat_arg(local_id) {
+            let local_scope_id = self.tcx().region_map.get(local_id);
+            self.ok_with_loan_of(cmt, ty::re_scope(local_scope_id), req_mutbl)
+          }
+          cat_stack_upvar(cmt) {
+            self.loan(cmt, req_mutbl) // NDM correct?
           }
           cat_discr(base, _) {
             self.loan(base, req_mutbl)
@@ -88,7 +116,7 @@ impl loan_methods for loan_ctxt {
           }
           cat_deref(cmt1, _, unsafe_ptr) |
           cat_deref(cmt1, _, gc_ptr) |
-          cat_deref(cmt1, _, region_ptr) {
+          cat_deref(cmt1, _, region_ptr(_)) {
             // Aliased data is simply not lendable.
             self.bccx.tcx.sess.span_bug(
                 cmt.span,
@@ -102,14 +130,17 @@ impl loan_methods for loan_ctxt {
     // Example: record fields.
     fn loan_stable_comp(cmt: cmt,
                         cmt_base: cmt,
-                        req_mutbl: ast::mutability) {
+                        req_mutbl: ast::mutability) -> bckres<()> {
         let base_mutbl = alt req_mutbl {
           m_imm { m_imm }
           m_const | m_mutbl { m_const }
         };
 
-        self.loan(cmt_base, base_mutbl);
-        self.ok_with_loan_of(cmt, req_mutbl)
+        do self.loan(cmt_base, base_mutbl).chain |_ok| {
+            // can use static for the scope because the base
+            // determines the lifetime, ultimately
+            self.ok_with_loan_of(cmt, ty::re_static, req_mutbl)
+        }
     }
 
     // An "unstable deref" means a deref of a ptr/comp where, if the
@@ -117,11 +148,13 @@ impl loan_methods for loan_ctxt {
     // deref would be invalidated. Examples: interior of variants, uniques.
     fn loan_unstable_deref(cmt: cmt,
                            cmt_base: cmt,
-                           req_mutbl: ast::mutability) {
+                           req_mutbl: ast::mutability) -> bckres<()> {
         // Variant components: the base must be immutable, because
         // if it is overwritten, the types of the embedded data
         // could change.
-        self.loan(cmt_base, m_imm);
-        self.ok_with_loan_of(cmt, req_mutbl)
+        do self.loan(cmt_base, m_imm).chain |_ok| {
+            // can use static, as in loan_stable_comp()
+            self.ok_with_loan_of(cmt, ty::re_static, req_mutbl)
+        }
     }
 }
diff --git a/src/rustc/middle/borrowck/preserve.rs b/src/rustc/middle/borrowck/preserve.rs
index 08d7227557b..860e4962295 100644
--- a/src/rustc/middle/borrowck/preserve.rs
+++ b/src/rustc/middle/borrowck/preserve.rs
@@ -1,78 +1,146 @@
 // ----------------------------------------------------------------------
 // Preserve(Ex, S) holds if ToAddr(Ex) will remain valid for the entirety of
 // the scope S.
-export public_methods;
+//
+
+export public_methods, preserve_condition, pc_ok, pc_if_pure;
+
+enum preserve_condition {
+    pc_ok,
+    pc_if_pure(bckerr)
+}
+
+impl public_methods for preserve_condition {
+    // combines two preservation conditions such that if either of
+    // them requires purity, the result requires purity
+    fn combine(pc: preserve_condition) -> preserve_condition {
+        alt self {
+          pc_ok => {pc}
+          pc_if_pure(e) => {self}
+        }
+    }
+}
 
 impl public_methods for borrowck_ctxt {
-    fn preserve(cmt: cmt, opt_scope_id: option<ast::node_id>) -> bckres<()> {
-        #debug["preserve(%s)", self.cmt_to_repr(cmt)];
+    fn preserve(cmt: cmt,
+                scope_region: ty::region,
+                item_ub: ast::node_id,
+                root_ub: ast::node_id)
+        -> bckres<preserve_condition> {
+
+        let ctxt = preserve_ctxt({bccx: self,
+                                  scope_region: scope_region,
+                                  item_ub: item_ub,
+                                  root_ub: root_ub,
+                                  root_managed_data: true});
+        (&ctxt).preserve(cmt)
+    }
+}
+
+enum preserve_ctxt = {
+    bccx: borrowck_ctxt,
+
+    // the region scope for which we must preserve the memory
+    scope_region: ty::region,
+
+    // the scope for the body of the enclosing fn/method item
+    item_ub: ast::node_id,
+
+    // the upper bound on how long we can root an @T pointer
+    root_ub: ast::node_id,
+
+    // if false, do not attempt to root managed data
+    root_managed_data: bool
+};
+
+
+impl private_methods for &preserve_ctxt {
+    fn tcx() -> ty::ctxt { self.bccx.tcx }
+
+    fn preserve(cmt: cmt) -> bckres<preserve_condition> {
+        #debug["preserve(cmt=%s, root_ub=%?, root_managed_data=%b)",
+               self.bccx.cmt_to_repr(cmt), self.root_ub,
+               self.root_managed_data];
         let _i = indenter();
 
         alt cmt.cat {
-          cat_rvalue | cat_special(_) {
-            ok(())
+          cat_special(sk_self) | cat_special(sk_heap_upvar) {
+            self.compare_scope(cmt, ty::re_scope(self.item_ub))
+          }
+          cat_special(sk_static_item) | cat_special(sk_method) {
+            ok(pc_ok)
+          }
+          cat_rvalue {
+            // when we borrow an rvalue, we can keep it rooted but only
+            // up to the root_ub point
+
+            // FIXME(#2977)--need to update trans!
+            self.compare_scope(cmt, ty::re_scope(self.root_ub))
           }
           cat_stack_upvar(cmt) {
-            self.preserve(cmt, opt_scope_id)
+            self.preserve(cmt)
           }
-          cat_local(_) {
+          cat_local(local_id) {
             // Normally, local variables are lendable, and so this
             // case should never trigger.  However, if we are
             // preserving an expression like a.b where the field `b`
             // has @ type, then it will recurse to ensure that the `a`
             // is stable to try and avoid rooting the value `a.b`.  In
-            // this case, opt_scope_id will be none.
-            if opt_scope_id.is_some() {
-                self.tcx.sess.span_bug(
+            // this case, root_managed_data will be false.
+            if self.root_managed_data {
+                self.tcx().sess.span_bug(
                     cmt.span,
-                    ~"preserve() called with local and \
-                      non-none opt_scope_id");
+                    ~"preserve() called with local and !root_managed_data");
             }
-            ok(())
+            let local_scope_id = self.tcx().region_map.get(local_id);
+            self.compare_scope(cmt, ty::re_scope(local_scope_id))
           }
-          cat_binding(_) {
+          cat_binding(local_id) {
             // Bindings are these kind of weird implicit pointers (cc
             // #2329).  We require (in gather_loans) that they be
             // rooted in an immutable location.
-            ok(())
+            let local_scope_id = self.tcx().region_map.get(local_id);
+            self.compare_scope(cmt, ty::re_scope(local_scope_id))
           }
-          cat_arg(_) {
+          cat_arg(local_id) {
             // This can happen as not all args are lendable (e.g., &&
-            // modes).  In that case, the caller guarantees stability.
-            // This is basically a deref of a region ptr.
-            ok(())
+            // modes).  In that case, the caller guarantees stability
+            // for at least the scope of the fn.  This is basically a
+            // deref of a region ptr.
+            let local_scope_id = self.tcx().region_map.get(local_id);
+            self.compare_scope(cmt, ty::re_scope(local_scope_id))
           }
           cat_comp(cmt_base, comp_field(*)) |
           cat_comp(cmt_base, comp_index(*)) |
           cat_comp(cmt_base, comp_tuple) {
             // Most embedded components: if the base is stable, the
             // type never changes.
-            self.preserve(cmt_base, opt_scope_id)
+            self.preserve(cmt_base)
           }
           cat_comp(cmt_base, comp_variant(enum_did)) {
-            if ty::enum_is_univariant(self.tcx, enum_did) {
-                self.preserve(cmt_base, opt_scope_id)
+            if ty::enum_is_univariant(self.tcx(), enum_did) {
+                self.preserve(cmt_base)
             } else {
                 // If there are multiple variants: overwriting the
                 // base could cause the type of this memory to change,
                 // so require imm.
-                self.require_imm(cmt, cmt_base, opt_scope_id, err_mut_variant)
+                self.require_imm(cmt, cmt_base, err_mut_variant)
             }
           }
           cat_deref(cmt_base, _, uniq_ptr) {
             // Overwriting the base could cause this memory to be
             // freed, so require imm.
-            self.require_imm(cmt, cmt_base, opt_scope_id, err_mut_uniq)
+            self.require_imm(cmt, cmt_base, err_mut_uniq)
           }
-          cat_deref(_, _, region_ptr) {
-            // References are always "stable" by induction (when the
-            // reference of type &MT was created, the memory must have
-            // been stable)
-            ok(())
+          cat_deref(_, _, region_ptr(region)) {
+            // References are always "stable" for lifetime `region` by
+            // induction (when the reference of type &MT was created,
+            // the memory must have been stable).
+            self.compare_scope(cmt, region)
           }
           cat_deref(_, _, unsafe_ptr) {
             // Unsafe pointers are the user's problem
-            ok(())
+            ok(pc_ok)
           }
           cat_deref(base, derefs, gc_ptr) {
             // GC'd pointers of type @MT: if this pointer lives in
@@ -80,13 +148,26 @@ impl public_methods for borrowck_ctxt {
             // 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)];
             if base.mutbl == m_imm {
-                alt self.preserve(base, none) {
-                  ok(()) {ok(())}
-                  err(_) {self.attempt_root(cmt, opt_scope_id, base, derefs)}
+                let non_rooting_ctxt =
+                    preserve_ctxt({root_managed_data: false with **self});
+                alt (&non_rooting_ctxt).preserve(base) {
+                  ok(pc_ok) => {
+                    ok(pc_ok)
+                  }
+                  ok(pc_if_pure(_)) {
+                    #debug["must root @T, otherwise purity req'd"];
+                    self.attempt_root(cmt, base, derefs)
+                  }
+                  err(e) => {
+                    #debug["must root @T, err: %s",
+                           self.bccx.bckerr_code_to_str(e.code)];
+                    self.attempt_root(cmt, base, derefs)
+                  }
                 }
             } else {
-                self.attempt_root(cmt, opt_scope_id, base, derefs)
+                self.attempt_root(cmt, base, derefs)
             }
           }
           cat_discr(base, alt_id) {
@@ -144,43 +225,124 @@ impl public_methods for borrowck_ctxt {
             // in the *arm* vs the *alt*.
 
             // current scope must be the arm, which is always a child of alt:
-            assert self.tcx.region_map.get(opt_scope_id.get()) == alt_id;
+            assert {
+                alt check self.scope_region {
+                  ty::re_scope(arm_id) => {
+                    self.tcx().region_map.get(arm_id) == alt_id
+                  }
+                  _ => {false}
+                }
+            };
 
-            self.preserve(base, some(alt_id))
+            let alt_rooting_ctxt =
+                preserve_ctxt({scope_region: ty::re_scope(alt_id)
+                               with **self});
+            (&alt_rooting_ctxt).preserve(base)
           }
         }
     }
-}
 
-impl private_methods for borrowck_ctxt {
+    /// Reqiures that `cmt` (which is a deref or subcomponent of
+    /// `base`) be found in an immutable location (that is, `base`
+    /// must be immutable).  Also requires that `base` itself is
+    /// preserved.
     fn require_imm(cmt: cmt,
                    cmt_base: cmt,
-                   opt_scope_id: option<ast::node_id>,
-                   code: bckerr_code) -> bckres<()> {
+                   code: bckerr_code) -> bckres<preserve_condition> {
         // Variant contents and unique pointers: must be immutably
         // rooted to a preserved address.
-        alt cmt_base.mutbl {
-          m_mutbl | m_const { err({cmt:cmt, code:code}) }
-          m_imm { self.preserve(cmt_base, opt_scope_id) }
+        alt self.preserve(cmt_base) {
+          // the base is preserved, but if we are not mutable then
+          // purity is required
+          ok(pc_ok) => {
+            alt cmt_base.mutbl {
+              m_mutbl | m_const => {
+                ok(pc_if_pure({cmt:cmt, code:code}))
+              }
+              m_imm => {
+                ok(pc_ok)
+              }
+            }
+          }
+
+          // the base requires purity too, that's fine
+          ok(pc_if_pure(e)) => {
+            ok(pc_if_pure(e))
+          }
+
+          // base is not stable, doesn't matter
+          err(e) => {
+            err(e)
+          }
         }
     }
 
-    fn attempt_root(cmt: cmt, opt_scope_id: option<ast::node_id>,
-                    base: cmt, derefs: uint) -> bckres<()> {
-        alt opt_scope_id {
-          some(scope_id) {
-            #debug["Inserting root map entry for %s: \
-                    node %d:%u -> scope %d",
-                   self.cmt_to_repr(cmt), base.id,
-                   derefs, scope_id];
+    /// Checks that the scope for which the value must be preserved
+    /// is a subscope of `scope_ub`; if so, success.
+    fn compare_scope(cmt: cmt,
+                     scope_ub: ty::region) -> bckres<preserve_condition> {
+        let region_map = self.tcx().region_map;
+        if region::subregion(region_map, scope_ub, self.scope_region) {
+            ok(pc_ok)
+        } else {
+            err({cmt:cmt, code:err_out_of_scope(scope_ub,
+                                                self.scope_region)})
+        }
+    }
+
+    /// Here, `cmt=*base` is always a deref of managed data (if
+    /// `derefs` != 0, then an auto-deref).  This routine determines
+    /// whether it is safe to MAKE cmt stable by rooting the pointer
+    /// `base`.  We can only do the dynamic root if the desired
+    /// lifetime `self.scope_region` is a subset of `self.root_ub`
+    /// scope; otherwise, it would either require that we hold the
+    /// value live for longer than the current fn or else potentially
+    /// require that an statically unbounded number of values be
+    /// rooted (if a loop exists).
+    fn attempt_root(cmt: cmt, base: cmt,
+                    derefs: uint) -> bckres<preserve_condition> {
+        if !self.root_managed_data {
+            // normally, there is a root_ub; the only time that this
+            // is none is when a boxed value is stored in an immutable
+            // location.  In that case, we will test to see if that
+            // immutable location itself can be preserved long enough
+            // in which case no rooting is necessary.  But there it
+            // would be sort of pointless to avoid rooting the inner
+            // box by rooting an outer box, as it would just keep more
+            // memory live than necessary, so we set root_ub to none.
+            ret err({cmt:cmt, code:err_root_not_permitted});
+        }
 
-            let rk = {id: base.id, derefs: derefs};
-            self.root_map.insert(rk, scope_id);
-            ok(())
+        let root_region = ty::re_scope(self.root_ub);
+        alt self.scope_region {
+          // we can only root values if the desired region is some concrete
+          // scope within the fn body
+          ty::re_scope(scope_id) => {
+            let region_map = self.tcx().region_map;
+            #debug["Considering root map entry for %s: \
+                    node %d:%u -> scope_id %?, root_ub %?",
+                   self.bccx.cmt_to_repr(cmt), base.id,
+                   derefs, scope_id, self.root_ub];
+            if region::subregion(region_map, root_region, self.scope_region) {
+                #debug["Elected to root"];
+                let rk = {id: base.id, derefs: derefs};
+                self.bccx.root_map.insert(rk, scope_id);
+                ret ok(pc_ok);
+            } else {
+                #debug["Unable to root"];
+                ret err({cmt:cmt,
+                         code:err_out_of_root_scope(root_region,
+                                                    self.scope_region)});
+            }
           }
-          none {
-            err({cmt:cmt, code:err_preserve_gc})
+
+          // we won't be able to root long enough
+          _ => {
+              ret err({cmt:cmt,
+                       code:err_out_of_root_scope(root_region,
+                                                  self.scope_region)});
           }
+
         }
     }
 }
diff --git a/src/rustc/middle/lang_items.rs b/src/rustc/middle/lang_items.rs
index 0aaacee019a..07704de1297 100644
--- a/src/rustc/middle/lang_items.rs
+++ b/src/rustc/middle/lang_items.rs
@@ -63,24 +63,19 @@ class LanguageItems {
 }
 
 class LanguageItemCollector {
-    let items: LanguageItems;
+    let items: &LanguageItems;
 
     let crate: @crate;
     let session: session;
 
     let item_refs: hashmap<~str,&mut option<def_id>>;
 
-    new(crate: @crate, session: session) {
+    new(crate: @crate, session: session, items: &self/LanguageItems) {
         self.crate = crate;
         self.session = session;
-
-        self.items = LanguageItems();
-
+        self.items = items;
         self.item_refs = str_hash();
-    }
 
-    // XXX: Needed to work around an issue with constructors.
-    fn init() {
         self.item_refs.insert(~"const", &mut self.items.const_trait);
         self.item_refs.insert(~"copy", &mut self.items.copy_trait);
         self.item_refs.insert(~"send", &mut self.items.send_trait);
@@ -206,7 +201,6 @@ class LanguageItemCollector {
     }
 
     fn collect() {
-        self.init();
         self.collect_local_language_items();
         self.collect_external_language_items();
         self.check_completeness();
@@ -214,8 +208,9 @@ class LanguageItemCollector {
 }
 
 fn collect_language_items(crate: @crate, session: session) -> LanguageItems {
-    let collector = LanguageItemCollector(crate, session);
+    let items = LanguageItems();
+    let collector = LanguageItemCollector(crate, session, &items);
     collector.collect();
-    copy collector.items
+    copy items
 }
 
diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs
index 4ee3b6adc31..d0dc1ea28f7 100644
--- a/src/rustc/middle/region.rs
+++ b/src/rustc/middle/region.rs
@@ -39,6 +39,15 @@ type ctxt = {
     def_map: resolve3::DefMap,
     region_map: region_map,
 
+    // Generally speaking, expressions are parented to their innermost
+    // enclosing block. But some kinds of expressions serve as
+    // parents: calls, methods, etc.  In addition, some expressions
+    // serve as parents by virtue of where they appear.  For example,
+    // the condition in a while loop is always a parent.  In those
+    // cases, we add the node id of such an expression to this set so
+    // that when we visit it we can view it as a parent.
+    root_exprs: hashmap<ast::node_id, ()>,
+
     // The parent scope is the innermost block, call, or alt
     // expression during the execution of which the current expression
     // will be evaluated.  Generally speaking, the innermost parent
@@ -87,6 +96,27 @@ fn scope_contains(region_map: region_map, superscope: ast::node_id,
     ret true;
 }
 
+/// Determines whether one region is a subregion of another.  This is
+/// intended to run *after inference* and sadly the logic is somewhat
+/// duplicated with the code in infer.rs.
+fn subregion(region_map: region_map,
+             super_region: ty::region,
+             sub_region: ty::region) -> bool {
+    super_region == sub_region ||
+        alt (super_region, sub_region) {
+          (ty::re_static, _) => {true}
+
+          (ty::re_scope(super_scope), ty::re_scope(sub_scope)) |
+          (ty::re_free(super_scope, _), ty::re_scope(sub_scope)) => {
+            scope_contains(region_map, super_scope, sub_scope)
+          }
+
+          _ => {
+            false
+          }
+        }
+}
+
 /// Finds the nearest common ancestor (if any) of two scopes.  That
 /// is, finds the smallest scope which is greater than or equal to
 /// both `scope_a` and `scope_b`.
@@ -198,31 +228,37 @@ fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
 
 fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
     record_parent(cx, expr.id);
+
+    let mut new_cx = cx;
     alt expr.node {
-      ast::expr_call(*) {
+      ast::expr_call(*) => {
         #debug["node %d: %s", expr.id, pprust::expr_to_str(expr)];
-        let new_cx = {parent: some(expr.id) with cx};
-        visit::visit_expr(expr, new_cx, visitor);
+        new_cx.parent = some(expr.id);
       }
-      ast::expr_alt(subexpr, _, _) {
+      ast::expr_alt(subexpr, _, _) => {
         #debug["node %d: %s", expr.id, pprust::expr_to_str(expr)];
-        let new_cx = {parent: some(expr.id) with cx};
-        visit::visit_expr(expr, new_cx, visitor);
+        new_cx.parent = some(expr.id);
       }
       ast::expr_fn(_, _, _, cap_clause) |
-      ast::expr_fn_block(_, _, cap_clause) {
+      ast::expr_fn_block(_, _, cap_clause) => {
         // although the capture items are not expressions per se, they
         // do get "evaluated" in some sense as copies or moves of the
         // relevant variables so we parent them like an expression
         for (*cap_clause).each |cap_item| {
-            record_parent(cx, cap_item.id);
+            record_parent(new_cx, cap_item.id);
         }
-        visit::visit_expr(expr, cx, visitor);
       }
-      _ {
-        visit::visit_expr(expr, cx, visitor);
+      ast::expr_while(cond, _) => {
+        new_cx.root_exprs.insert(cond.id, ());
       }
+      _ => {}
+    };
+
+    if new_cx.root_exprs.contains_key(expr.id) {
+        new_cx.parent = some(expr.id);
     }
+
+    visit::visit_expr(expr, new_cx, visitor);
 }
 
 fn resolve_local(local: @ast::local, cx: ctxt, visitor: visit::vt<ctxt>) {
@@ -269,6 +305,7 @@ fn resolve_crate(sess: session, def_map: resolve3::DefMap,
     let cx: ctxt = {sess: sess,
                     def_map: def_map,
                     region_map: int_hash(),
+                    root_exprs: int_hash(),
                     parent: none};
     let visitor = visit::mk_vt(@{
         visit_block: resolve_block,
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index fdbab1a789f..e58de56703a 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -213,9 +213,11 @@ enum ast_ty_to_ty_cache_entry {
     atttce_resolved(t)  /* resolved to a type, irrespective of region */
 }
 
-#[auto_serialize]
+// N.B.: Borrows from inlined content are not accurately deserialized.  This
+// is because we don't need the details in trans, we only care if there is an
+// entry in the table or not.
 type borrow = {
-    scope_id: ast::node_id,
+    region: ty::region,
     mutbl: ast::mutability
 };
 
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index d472991d85f..e7575ff0f1f 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -105,7 +105,6 @@ type fn_ctxt_ =
      // use.  In practice, this is the innermost loop or function
      // body.
      mut region_lb: ast::node_id,
-     mut region_ub: ast::node_id,
 
      in_scope_regions: isr_alist,
 
@@ -130,7 +129,6 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t,
                infcx: infer::new_infer_ctxt(ccx.tcx),
                locals: int_hash(),
                mut region_lb: region_bnd,
-               mut region_ub: region_bnd,
                in_scope_regions: @nil,
                node_types: map::int_hash(),
                node_type_substs: map::int_hash(),
@@ -246,7 +244,6 @@ fn check_fn(ccx: @crate_ctxt,
                    infcx: infcx,
                    locals: locals,
                    mut region_lb: body.node.id,
-                   mut region_ub: body.node.id,
                    in_scope_regions: isr,
                    node_types: node_types,
                    node_type_substs: node_type_substs,
@@ -600,15 +597,13 @@ impl methods for @fn_ctxt {
 
     fn mk_assignty(expr: @ast::expr, borrow_lb: ast::node_id,
                    sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
-        let anmnt = {expr_id: expr.id, borrow_lb: borrow_lb,
-                     borrow_ub: self.region_ub};
+        let anmnt = {expr_id: expr.id, span: expr.span, borrow_lb: borrow_lb};
         infer::mk_assignty(self.infcx, anmnt, sub, sup)
     }
 
     fn can_mk_assignty(expr: @ast::expr, borrow_lb: ast::node_id,
-                      sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
-        let anmnt = {expr_id: expr.id, borrow_lb: borrow_lb,
-                     borrow_ub: self.region_ub};
+                       sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
+        let anmnt = {expr_id: expr.id, span: expr.span, borrow_lb: borrow_lb};
         infer::can_mk_assignty(self.infcx, anmnt, sub, sup)
     }
 
@@ -637,13 +632,6 @@ impl methods for @fn_ctxt {
         self.region_lb = old_region_lb;
         ret v;
     }
-    fn with_region_ub<R>(ub: ast::node_id, f: fn() -> R) -> R {
-        let old_region_ub = self.region_ub;
-        self.region_ub = ub;
-        let v <- f();
-        self.region_ub = old_region_ub;
-        ret v;
-    }
 }
 
 fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
@@ -1376,7 +1364,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         bot = check_expr(fcx, oprnd, unpack_expected(fcx, expected, |ty|
             alt ty { ty::ty_rptr(_, mt) { some(mt.ty) } _ { none } }
         ));
-        let region = region_of(fcx, oprnd);
+        //let region = region_of(fcx, oprnd);
+        let region = fcx.infcx.next_region_var_with_scope_lb(expr.id);
         let tm = { ty: fcx.expr_ty(oprnd), mutbl: mutbl };
         let oprnd_t = ty::mk_rptr(tcx, region, tm);
         fcx.write_ty(id, oprnd_t);
@@ -1446,15 +1435,11 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
       }
       ast::expr_while(cond, body) {
         bot = check_expr_with(fcx, cond, ty::mk_bool(tcx));
-        do fcx.with_region_ub(body.node.id) {
-            check_block_no_value(fcx, body);
-        }
+        check_block_no_value(fcx, body);
         fcx.write_ty(id, ty::mk_nil(tcx));
       }
       ast::expr_loop(body) {
-        do fcx.with_region_ub(body.node.id) {
-            check_block_no_value(fcx, body);
-        }
+        check_block_no_value(fcx, body);
         fcx.write_ty(id, ty::mk_nil(tcx));
         bot = !may_break(body);
       }
diff --git a/src/rustc/middle/typeck/infer.rs b/src/rustc/middle/typeck/infer.rs
index 79df5975307..77b7f5cec3b 100644
--- a/src/rustc/middle/typeck/infer.rs
+++ b/src/rustc/middle/typeck/infer.rs
@@ -276,13 +276,13 @@ fn convert_integral_ty_to_int_ty_set(tcx: ty::ctxt, t: ty::t)
 }
 
 // Extra information needed to perform an assignment that may borrow.
-// The `expr_id` is the is of the expression whose type is being
-// assigned, and `borrow_scope` is the region scope to use if the
-// value should be borrowed.
+// The `expr_id` and `span` are the id/span of the expression
+// whose type is being assigned, and `borrow_scope` is the region
+// scope to use if the value should be borrowed.
 type assignment = {
     expr_id: ast::node_id,
+    span: span,
     borrow_lb: ast::node_id,
-    borrow_ub: ast::node_id,
 };
 
 type bound<T:copy> = option<T>;
@@ -325,6 +325,7 @@ enum infer_ctxt = @{
     region_var_counter: @mut uint,
 
     borrowings: dvec<{expr_id: ast::node_id,
+                      span: span,
                       scope: ty::region,
                       mutbl: ast::mutability}>
 };
@@ -422,16 +423,18 @@ fn resolve_region(cx: infer_ctxt, r: ty::region, modes: uint)
 fn resolve_borrowings(cx: infer_ctxt) {
     for cx.borrowings.each |item| {
         alt resolve_region(cx, item.scope, resolve_all|force_all) {
-          ok(ty::re_scope(scope_id)) => {
-            #debug["borrowing for expr %d resolved to scope %d, mutbl %?",
-                   item.expr_id, scope_id, item.mutbl];
+          ok(region) => {
+            #debug["borrowing for expr %d resolved to region %?, mutbl %?",
+                   item.expr_id, region, item.mutbl];
             cx.tcx.borrowings.insert(
-                item.expr_id, {scope_id: scope_id, mutbl: item.mutbl});
+                item.expr_id, {region: region, mutbl: item.mutbl});
           }
 
-          r => {
-            cx.tcx.sess.bug(
-                #fmt["borrowing resolved to %?, not a valid scope", r]);
+          err(e) => {
+            let str = fixup_err_to_str(e);
+            cx.tcx.sess.span_err(
+                item.span,
+                #fmt["could not resolve lifetime for borrow: %s", str]);
           }
         }
     }
@@ -663,6 +666,11 @@ impl methods for infer_ctxt {
         ret region_vid(id);
     }
 
+    fn next_region_var_with_scope_lb(scope_id: ast::node_id) -> ty::region {
+        self.next_region_var({lb: some(ty::re_scope(scope_id)),
+                              ub: none})
+    }
+
     fn next_region_var(bnds: bounds<ty::region>) -> ty::region {
         ty::re_var(self.next_region_var_id(bnds))
     }
@@ -1475,10 +1483,7 @@ impl assignment for infer_ctxt {
             do self.sub_tys(a, nr_b).then {
                 // Create a fresh region variable `r_a` with the given
                 // borrow bounds:
-                let r_lb = ty::re_scope(anmnt.borrow_lb);
-                let r_ub = ty::re_scope(anmnt.borrow_ub);
-                let r_a = self.next_region_var({lb: some(r_lb),
-                                                ub: some(r_ub)});
+                let r_a = self.next_region_var_with_scope_lb(anmnt.borrow_lb);
 
                 #debug["anmnt=%?", anmnt];
                 do sub(self).contraregions(r_a, r_b).chain |_r| {
@@ -1487,6 +1492,7 @@ impl assignment for infer_ctxt {
                     #debug["borrowing expression #%?, scope=%?, m=%?",
                            anmnt, r_a, m];
                     self.borrowings.push({expr_id: anmnt.expr_id,
+                                          span: anmnt.span,
                                           scope: r_a,
                                           mutbl: m});
                     uok()
diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs
index b114e659c4c..d3c05ea6bec 100644
--- a/src/rustc/util/ppaux.rs
+++ b/src/rustc/util/ppaux.rs
@@ -62,9 +62,10 @@ fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> ~str {
             #fmt("<method at %s>",
                  codemap::span_to_str(expr.span, cx.sess.codemap))
           }
-          _ { cx.sess.bug(
-              #fmt["re_scope refers to %s",
-                   ast_map::node_id_to_str(cx.items, node_id)]) }
+          _ {
+            #fmt("<expression at %s>",
+                 codemap::span_to_str(expr.span, cx.sess.codemap))
+          }
         }
       }
       none {
diff --git a/src/test/compile-fail/borrowck-addr-of-upvar.rs b/src/test/compile-fail/borrowck-addr-of-upvar.rs
new file mode 100644
index 00000000000..ba64e888775
--- /dev/null
+++ b/src/test/compile-fail/borrowck-addr-of-upvar.rs
@@ -0,0 +1,14 @@
+fn foo(x: @int) -> fn@() -> &static/int {
+    fn@() -> &static/int {&*x} //~ ERROR illegal borrow
+}
+
+fn bar(x: @int) -> fn@() -> &int {
+    fn@() -> &int {&*x} //~ ERROR illegal borrow
+}
+
+fn zed(x: @int) -> fn@() -> int {
+    fn@() -> int {*&*x}
+}
+
+fn main() {
+}
\ No newline at end of file
diff --git a/src/test/compile-fail/borrowck-lend-flow.rs b/src/test/compile-fail/borrowck-lend-flow.rs
index 9a0af32e78b..0a489630c38 100644
--- a/src/test/compile-fail/borrowck-lend-flow.rs
+++ b/src/test/compile-fail/borrowck-lend-flow.rs
@@ -24,10 +24,25 @@ fn post_aliased_mut() {
 }
 
 fn post_aliased_scope(cond: bool) {
-    // NDM--scope of &
     let mut v = ~3;
-    borrow(v);  //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    if cond { inc(&mut v); } //~ NOTE prior loan as mutable granted here
+    borrow(v);
+    if cond { inc(&mut v); }
+}
+
+fn loop_overarching_alias_mut() {
+    let mut v = ~3;
+    let mut _x = &mut v; //~ NOTE prior loan as mutable granted here
+    loop {
+        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    }
+}
+
+fn block_overarching_alias_mut() {
+    let mut v = ~3;
+    let mut _x = &mut v; //~ NOTE prior loan as mutable granted here
+    for 3.times {
+        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    }
 }
 
 fn loop_aliased_mut() {
@@ -63,7 +78,7 @@ fn loop_in_block() {
     let mut v = ~3, w = ~4;
     let mut _x = &mut w;
     for uint::range(0u, 10u) |_i| {
-        borrow(v); //~ ERROR loan of captured outer mutable variable in a stack closure as immutable conflicts with prior loan
+        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
         _x = &mut v; //~ NOTE prior loan as mutable granted here
     }
 }
@@ -77,7 +92,7 @@ fn at_most_once_block() {
     let mut v = ~3, w = ~4;
     let mut _x = &mut w;
     do at_most_once {
-        borrow(v); //~ ERROR loan of captured outer mutable variable in a stack closure as immutable conflicts with prior loan
+        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
         _x = &mut v; //~ NOTE prior loan as mutable granted here
     }
 }
diff --git a/src/test/compile-fail/issue-511.rs b/src/test/compile-fail/issue-511.rs
index 9dea2443187..70434074dbf 100644
--- a/src/test/compile-fail/issue-511.rs
+++ b/src/test/compile-fail/issue-511.rs
@@ -8,6 +8,5 @@ fn f<T>(&o: option<T>) {
 fn main() {
     f::<int>(option::none);
     //~^ ERROR taking mut reference to static item
-    //~^^ ERROR illegal borrow unless pure: creating mutable alias to aliasable, immutable memory
-    //~^^^ NOTE impure due to access to impure function
+    //~^^ ERROR illegal borrow: creating mutable alias to aliasable, immutable memory
 }
\ No newline at end of file
diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs
index 295e1749d4b..8383a500e80 100644
--- a/src/test/compile-fail/kindck-owned-trait-contains.rs
+++ b/src/test/compile-fail/kindck-owned-trait-contains.rs
@@ -13,7 +13,7 @@ fn main() {
     // Here, an error results as the type of y is inferred to
     // repeater<&lt/3> where lt is the block.
     let y = { //~ ERROR reference is not valid outside of its lifetime
-        let x = &3;
+        let x: &blk/int = &3;
         repeater(@x)
     };
     assert 3 == *(y.get()); //~ ERROR reference is not valid outside of its lifetime
diff --git a/src/test/compile-fail/regions-addr-of-arg.rs b/src/test/compile-fail/regions-addr-of-arg.rs
index 6ef6e219e13..46ba84c2c18 100644
--- a/src/test/compile-fail/regions-addr-of-arg.rs
+++ b/src/test/compile-fail/regions-addr-of-arg.rs
@@ -1,5 +1,5 @@
 fn foo(a: int) {
-    let _p: &static/int = &a; //~ ERROR mismatched types
+    let _p: &static/int = &a; //~ ERROR illegal borrow
 }
 
 fn bar(a: int) {
diff --git a/src/test/compile-fail/regions-addr-of-self.rs b/src/test/compile-fail/regions-addr-of-self.rs
index 969361db63e..fbef015ea7f 100644
--- a/src/test/compile-fail/regions-addr-of-self.rs
+++ b/src/test/compile-fail/regions-addr-of-self.rs
@@ -6,7 +6,7 @@ class dog {
     }
 
     fn chase_cat() {
-        let p: &static/mut uint = &mut self.cats_chased; //~ ERROR mismatched types
+        let p: &static/mut uint = &mut self.cats_chased; //~ ERROR illegal borrow
         *p += 1u;
     }
 
@@ -20,4 +20,4 @@ fn main() {
     let d = dog();
     d.chase_cat();
     #debug["cats_chased: %u", d.cats_chased];
-}
\ No newline at end of file
+}
diff --git a/src/test/compile-fail/regions-addr-of-upvar-self.rs b/src/test/compile-fail/regions-addr-of-upvar-self.rs
index e61d5eb3d05..e44674919be 100644
--- a/src/test/compile-fail/regions-addr-of-upvar-self.rs
+++ b/src/test/compile-fail/regions-addr-of-upvar-self.rs
@@ -6,12 +6,12 @@ class dog {
     }
 
     fn chase_cat() {
-        for uint::range(0u, 10u) |i| {
-            let p: &static/mut uint = &mut self.food; //~ ERROR mismatched types
+        for uint::range(0u, 10u) |_i| {
+            let p: &static/mut uint = &mut self.food; //~ ERROR illegal borrow
             *p = 3u;
         }
     }
 }
 
 fn main() {
-}
\ No newline at end of file
+}
diff --git a/src/test/compile-fail/regions-appearance-constraint.rs b/src/test/compile-fail/regions-appearance-constraint.rs
deleted file mode 100644
index 300aece8c3c..00000000000
--- a/src/test/compile-fail/regions-appearance-constraint.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-
-Tests that borrowing always produces a pointer confined to the
-innermost scope.  In this case, the variable `a` gets inferred
-to the lifetime of the `if` statement because it is assigned
-a borrow of `y` which takes place within the `if`.
-
-Note: If this constraint were lifted (as I contemplated at one point),
-it complicates the preservation mechanics in trans, though not
-irreperably.  I'm partially including this test so that if these
-semantics do change we'll remember to test this scenario.
-
-*/
-
-fn testfn(cond: bool) {
-    let mut x = @3;
-    let mut y = @4;
-
-    let mut a = &*x;
-    //~^ ERROR reference is not valid outside of its lifetime
-
-    let mut exp = 3;
-    if cond {
-        a = &*y;
-
-        exp = 4;
-    }
-
-    x = @5;
-    y = @6;
-    assert *a == exp;
-}
-
-fn main() {
-}
\ No newline at end of file
diff --git a/src/test/compile-fail/regions-creating-enums.rs b/src/test/compile-fail/regions-creating-enums.rs
index 16d0bd6afde..5924164ef26 100644
--- a/src/test/compile-fail/regions-creating-enums.rs
+++ b/src/test/compile-fail/regions-creating-enums.rs
@@ -20,12 +20,12 @@ fn compute(x: &ast) -> uint {
 fn map_nums(x: &ast, f: fn(uint) -> uint) -> &ast {
     alt *x {
       num(x) {
-        ret &num(f(x)); //~ ERROR mismatched types: expected `&ast/&` but found
+        ret &num(f(x)); //~ ERROR illegal borrow
       }
       add(x, y) {
         let m_x = map_nums(x, f);
         let m_y = map_nums(y, f);
-        ret &add(m_x, m_y);  //~ ERROR mismatched types: expected `&ast/&` but found
+        ret &add(m_x, m_y);  //~ ERROR illegal borrow
       }
     }
 }
diff --git a/src/test/compile-fail/regions-escape-loop-via-variable.rs b/src/test/compile-fail/regions-escape-loop-via-variable.rs
index 04900da8ae5..e63a0717503 100644
--- a/src/test/compile-fail/regions-escape-loop-via-variable.rs
+++ b/src/test/compile-fail/regions-escape-loop-via-variable.rs
@@ -4,11 +4,10 @@ fn main() {
     // Here, the variable `p` gets inferred to a type with a lifetime
     // of the loop body.  The regionck then determines that this type
     // is invalid.
-    let mut p = //~ ERROR reference is not valid
-        &x;
+    let mut p = &x;
 
     loop {
         let x = 1 + *p;
-        p = &x;
+        p = &x; //~ ERROR illegal borrow
     }
 }
diff --git a/src/test/compile-fail/regions-escape-loop-via-vec.rs b/src/test/compile-fail/regions-escape-loop-via-vec.rs
index 5f57a3e87b1..638f14f8794 100644
--- a/src/test/compile-fail/regions-escape-loop-via-vec.rs
+++ b/src/test/compile-fail/regions-escape-loop-via-vec.rs
@@ -1,17 +1,13 @@
 // The type of `y` ends up getting inferred to the type of the block.
-// This generates a ton of error msgs at the moment.
 fn broken() -> int {
     let mut x = 3;
-    let mut y = ~[&mut x]; //~ ERROR reference is not valid
+    let mut y = ~[&mut x];
     while x < 10 {
         let mut z = x;
-        y += ~[&mut z];
+        y += ~[&mut z]; //~ ERROR illegal borrow
         x += 1;
     }
     vec::foldl(0, y, |v, p| v + *p )
-    //~^ ERROR reference is not valid
-    //~^^ ERROR reference is not valid
-    //~^^^ ERROR reference is not valid
 }
 
 fn main() { }
\ No newline at end of file
diff --git a/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
index aeb864d92a7..2f4003f0f3a 100644
--- a/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
+++ b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
@@ -5,9 +5,9 @@ fn x_coord(p: &point) -> &int {
 }
 
 fn foo(p: @point) -> &int {
-    let xc = x_coord(p);
+    let xc = x_coord(p); //~ ERROR illegal borrow
     assert *xc == 3;
-    ret xc; //~ ERROR mismatched types: expected `&int` but found
+    ret xc;
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
index e89d801b361..0c62532f86a 100644
--- a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
+++ b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
@@ -1,11 +1,18 @@
 fn borrow<T>(x: &T) -> &T {x}
 
-fn main() {
-    let x = @3;
-    let y: &int; //~ ERROR reference is not valid outside of its lifetime
-    while true {
-        y = borrow(x);
+fn foo(cond: fn() -> bool, box: fn() -> @int) {
+    let mut y: &int;
+    loop {
+        let x = box();
+
+	// Here we complain because the resulting region
+	// of this borrow is the fn body as a whole.
+        y = borrow(x); //~ ERROR managed value would have to be rooted for lifetime 
+
         assert *x == *y;
+        if cond() { break; }
     }
-    assert *x == *y;
+    assert *y != 0;
 }
+
+fn main() {}
diff --git a/src/test/compile-fail/regions-nested-fns-2.rs b/src/test/compile-fail/regions-nested-fns-2.rs
new file mode 100644
index 00000000000..ad22140b3b5
--- /dev/null
+++ b/src/test/compile-fail/regions-nested-fns-2.rs
@@ -0,0 +1,11 @@
+fn ignore<T>(_t: T) {}
+
+fn nested() {
+    let y = 3;
+    ignore(fn&(z: &z/int) -> &z/int {
+        if false { ret &y; } //~ ERROR illegal borrow
+        ret z;
+    });
+}
+
+fn main() {}
\ No newline at end of file
diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs
index 2d0fc08f597..ae3102b8ec2 100644
--- a/src/test/compile-fail/regions-nested-fns.rs
+++ b/src/test/compile-fail/regions-nested-fns.rs
@@ -12,7 +12,6 @@ fn nested(x: &x/int) {
 
     ignore(fn&(z: &z/int) -> &z/int {
         if false { ret x; }  //~ ERROR references with lifetime
-        if false { ret &y; } //~ ERROR references with lifetime
         if false { ret ay; } //~ ERROR references with lifetime
         ret z;
     });
diff --git a/src/test/compile-fail/regions-ret.rs b/src/test/compile-fail/regions-ret.rs
index becfa8e7671..9c1bf26b400 100644
--- a/src/test/compile-fail/regions-ret.rs
+++ b/src/test/compile-fail/regions-ret.rs
@@ -1,7 +1,5 @@
-// error-pattern: mismatched types
-
-fn f(x : &a/int) -> &a/int {
-    ret &3;
+fn f(_x : &a/int) -> &a/int {
+    ret &3; //~ ERROR illegal borrow
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-scoping.rs b/src/test/compile-fail/regions-scoping.rs
index ac9bef8ca5d..8526c6722f4 100644
--- a/src/test/compile-fail/regions-scoping.rs
+++ b/src/test/compile-fail/regions-scoping.rs
@@ -33,10 +33,15 @@ fn nested(x: &x/int) {  // (1)
         let z = 3i;
         let d: &x/int = foo(x, x, |_x, _y, z| z );
         let e: &x/int = foo(x, &z, |_x, _y, z| z );
-        let f: &x/int = foo(&z, &z, |_x, _y, z| z ); //~ ERROR mismatched types: expected `&x/int` but found
+
+        // This would result in an error, but it is not reported by typeck
+        // anymore but rather borrowck. Therefore, it doesn't end up
+        // getting printed out since compilation fails after typeck.
+        //
+        // let f: &x/int = foo(&z, &z, |_x, _y, z| z ); // ERROR mismatched types: expected `&x/int` but found
 
         foo(x, &z, |x, _y, _z| x ); //~ ERROR mismatched types: expected `&z/int` but found `&x/int`
-        foo(x, &z, |_x, y, _z| y ); //~ ERROR mismatched types: expected `&z/int` but found `&<block at
+        foo(x, &z, |_x, y, _z| y ); //~ ERROR mismatched types: expected `&z/int` but found `&<expression at
     }
 }
 
diff --git a/src/test/run-pass/borrowck-root-while-cond-2.rs b/src/test/run-pass/borrowck-root-while-cond-2.rs
new file mode 100644
index 00000000000..a26ee45d029
--- /dev/null
+++ b/src/test/run-pass/borrowck-root-while-cond-2.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let rec = @{mut f: @{g: ~[1, 2, 3]}};
+    while rec.f.g.len() == 23 {}
+}
diff --git a/src/test/run-pass/borrowck-root-while-cond.rs b/src/test/run-pass/borrowck-root-while-cond.rs
new file mode 100644
index 00000000000..07016d1bf0d
--- /dev/null
+++ b/src/test/run-pass/borrowck-root-while-cond.rs
@@ -0,0 +1,6 @@
+fn borrow<T>(x: &T) -> &T {x}
+
+fn main() {
+    let rec = @{mut f: @22};
+    while *borrow(rec.f) == 23 {}
+}
diff --git a/src/test/run-pass/regions-addr-of-interior-of-unique-box.rs b/src/test/run-pass/regions-addr-of-interior-of-unique-box.rs
new file mode 100644
index 00000000000..e9a3d0b3ec3
--- /dev/null
+++ b/src/test/run-pass/regions-addr-of-interior-of-unique-box.rs
@@ -0,0 +1,13 @@
+type point = { x: int, y: int };
+type character = { pos: ~point };
+
+fn get_x(x: &character) -> &int {
+    // interesting case because the scope of this
+    // borrow of the unique pointer is in fact
+    // larger than the fn itself
+    ret &x.pos.x;
+}
+
+fn main() {
+}
+
diff --git a/src/test/run-pass/regions-appearance-constraint.rs b/src/test/run-pass/regions-appearance-constraint.rs
new file mode 100644
index 00000000000..2a8258b3936
--- /dev/null
+++ b/src/test/run-pass/regions-appearance-constraint.rs
@@ -0,0 +1,22 @@
+/* Tests conditional rooting of the box y */
+
+fn testfn(cond: bool) {
+    let mut x = @3;
+    let mut y = @4;
+
+    let mut a = &*x;
+
+    let mut exp = 3;
+    if cond {
+        a = &*y;
+
+        exp = 4;
+    }
+
+    x = @5;
+    y = @6;
+    assert *a == exp;
+}
+
+fn main() {
+}
diff --git a/src/test/run-pass/regions-infer-borrow-scope-addr-of.rs b/src/test/run-pass/regions-infer-borrow-scope-addr-of.rs
new file mode 100644
index 00000000000..124c271032f
--- /dev/null
+++ b/src/test/run-pass/regions-infer-borrow-scope-addr-of.rs
@@ -0,0 +1,19 @@
+fn main() {
+    let mut x = 4;
+
+    for uint::range(0, 3) |i| {
+        // ensure that the borrow in this alt
+	// does not inferfere with the swap
+	// below.  note that it would it you
+	// naively borrowed &x for the lifetime
+	// of the variable x, as we once did
+        alt i {
+          i {
+            let y = &x;
+            assert i < *y;
+          }
+        }
+        let mut y = 4;
+        y <-> x;
+    }
+}