about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-07-06 09:14:57 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-07-14 17:37:32 -0700
commit3ef7ff8b892aa2ad7e821d8858c349e22d1197ba (patch)
tree41c4ef19b8c978ecd84daf6c2f103d7ecdb704da
parent41a21f053ced3df8fe9acc66cb30fb6005339b3e (diff)
downloadrust-3ef7ff8b892aa2ad7e821d8858c349e22d1197ba.tar.gz
rust-3ef7ff8b892aa2ad7e821d8858c349e22d1197ba.zip
infer the scope of borrows
-rw-r--r--src/rustc/middle/typeck/check.rs160
-rw-r--r--src/rustc/middle/typeck/check/demand.rs5
-rw-r--r--src/rustc/middle/typeck/check/method.rs10
-rw-r--r--src/rustc/middle/typeck/check/regionck.rs38
-rw-r--r--src/rustc/middle/typeck/check/vtable.rs5
-rw-r--r--src/rustc/middle/typeck/check/writeback.rs13
-rw-r--r--src/rustc/middle/typeck/coherence.rs2
-rw-r--r--src/rustc/middle/typeck/infer.rs270
-rw-r--r--src/rustc/util/ppaux.rs6
-rw-r--r--src/test/compile-fail/regions-borrow.rs8
-rw-r--r--src/test/compile-fail/regions-infer-borrow-scope-too-big.rs14
-rw-r--r--src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs11
-rw-r--r--src/test/run-pass/regions-escape-into-other-fn.rs (renamed from src/test/compile-fail/regions-escape-into-other-fn.rs)2
-rw-r--r--src/test/run-pass/regions-infer-borrow-scope-view.rs9
-rw-r--r--src/test/run-pass/regions-infer-borrow-scope-within-loop-ok.rs10
-rw-r--r--src/test/run-pass/regions-infer-borrow-scope.rs12
16 files changed, 375 insertions, 200 deletions
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index 4f5ea3fe4f8..bf2e4cb0e33 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -74,8 +74,7 @@ import rscope::{anon_rscope, binding_rscope, empty_rscope, in_anon_rscope};
 import rscope::{in_binding_rscope, region_scope, type_rscope};
 import syntax::ast::ty_i;
 import typeck::infer::{unify_methods}; // infcx.set()
-import typeck::infer::{force_level, force_none, force_ty_vars_only,
-                       force_all};
+import typeck::infer::{resolve_type, force_tvar};
 
 type fn_ctxt =
     // var_bindings, locals and next_var_id are shared
@@ -89,7 +88,22 @@ type fn_ctxt =
      infcx: infer::infer_ctxt,
      locals: hashmap<ast::node_id, tv_vid>,
 
-     mut blocks: ~[ast::node_id], // stack of blocks in scope, may be empty
+     // Sometimes we generate region pointers where the precise region
+     // to use is not known. For example, an expression like `&x.f`
+     // where `x` is of type `@T`: in this case, we will be rooting
+     // `x` onto the stack frame, and we could choose to root it until
+     // the end of (almost) any enclosing block or expression.  We
+     // want to pick the narrowest block that encompasses all uses.
+     //
+     // What we do in such cases is to generate a region variable and
+     // assign it the following two fields as bounds.  The lower bound
+     // is always the innermost enclosing expression.  The upper bound
+     // is the outermost enclosing expression that we could legally
+     // 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,
 
      node_types: smallintmap::smallintmap<ty::t>,
@@ -98,7 +112,8 @@ type fn_ctxt =
      ccx: @crate_ctxt};
 
 // Used by check_const and check_enum_variants
-fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t) -> @fn_ctxt {
+fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t,
+                 region_bnd: ast::node_id) -> @fn_ctxt {
 // It's kind of a kludge to manufacture a fake function context
 // and statement context, but we might as well do write the code only once
     @{self_ty: none,
@@ -107,7 +122,8 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t) -> @fn_ctxt {
       purity: ast::pure_fn,
       infcx: infer::new_infer_ctxt(ccx.tcx),
       locals: int_hash(),
-      mut blocks: ~[],
+      mut region_lb: region_bnd,
+      mut region_ub: region_bnd,
       in_scope_regions: @nil,
       node_types: smallintmap::mk(),
       node_type_substs: map::int_hash(),
@@ -217,7 +233,8 @@ fn check_fn(ccx: @crate_ctxt,
           purity: purity,
           infcx: infcx,
           locals: locals,
-          mut blocks: ~[],
+          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,
@@ -307,9 +324,12 @@ fn check_fn(ccx: @crate_ctxt,
         };
 
         let visit_block = fn@(b: ast::blk, &&e: (), v: visit::vt<()>) {
-            vec::push(fcx.blocks, b.node.id);
-            visit::visit_block(b, e, v);
-            vec::pop(fcx.blocks);
+            // non-obvious: the `blk` variable maps to region lb, so
+            // we have to keep this up-to-date.  This
+            // is... unfortunate.  It'd be nice to not need this.
+            do fcx.with_region_lb(b.node.id) {
+                visit::visit_block(b, e, v);
+            }
         };
 
         // Don't descend into fns and items
@@ -430,7 +450,7 @@ impl of ast_conv for @fn_ctxt {
 
 impl of region_scope for @fn_ctxt {
     fn anon_region() -> result<ty::region, ~str> {
-        result::ok(self.infcx.next_region_var())
+        result::ok(self.infcx.next_region_var_nb())
     }
     fn named_region(id: ast::ident) -> result<ty::region, ~str> {
         do empty_rscope.named_region(id).chain_err |_e| {
@@ -448,10 +468,7 @@ impl of region_scope for @fn_ctxt {
 impl methods for @fn_ctxt {
     fn tag() -> ~str { #fmt["%x", ptr::addr_of(*self) as uint] }
     fn block_region() -> result<ty::region, ~str> {
-        alt vec::last_opt(self.blocks) {
-          some(bid) { result::ok(ty::re_scope(bid)) }
-          none { result::err(~"no block is in scope here") }
-        }
+        result::ok(ty::re_scope(self.region_lb))
     }
     #[inline(always)]
     fn write_ty(node_id: ast::node_id, ty: ty::t) {
@@ -534,15 +551,17 @@ impl methods for @fn_ctxt {
         infer::can_mk_subty(self.infcx, sub, sup)
     }
 
-    fn mk_assignty(expr: @ast::expr, borrow_scope: ast::node_id,
+    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_scope: borrow_scope};
+        let anmnt = {expr_id: expr.id, borrow_lb: borrow_lb,
+                     borrow_ub: self.region_ub};
         infer::mk_assignty(self.infcx, anmnt, sub, sup)
     }
 
-    fn can_mk_assignty(expr: @ast::expr, borrow_scope: ast::node_id,
+    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_scope: borrow_scope};
+        let anmnt = {expr_id: expr.id, borrow_lb: borrow_lb,
+                     borrow_ub: self.region_ub};
         infer::can_mk_assignty(self.infcx, anmnt, sub, sup)
     }
 
@@ -564,6 +583,20 @@ impl methods for @fn_ctxt {
           }
         }
     }
+    fn with_region_lb<R>(lb: ast::node_id, f: fn() -> R) -> R {
+        let old_region_lb = self.region_lb;
+        self.region_lb = lb;
+        let v <- f();
+        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 {
@@ -682,7 +715,7 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id) -> ty_param_substs_and_ty {
          raw_ty: ity.ty}
     };
 
-    let self_r = if rp {some(fcx.infcx.next_region_var())} else {none};
+    let self_r = if rp {some(fcx.infcx.next_region_var_nb())} else {none};
     let tps = fcx.infcx.next_ty_vars(n_tps);
 
     let substs = {self_r: self_r, self_ty: none, tps: tps};
@@ -733,7 +766,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
               sty @ ty::ty_fn(fn_ty) {
                 replace_bound_regions_in_fn_ty(
                     fcx.ccx.tcx, @nil, none, fn_ty,
-                    |_br| fcx.infcx.next_region_var()).fn_ty
+                    |_br| fcx.infcx.next_region_var_nb()).fn_ty
               }
               sty {
                 // I would like to make this span_err, but it's
@@ -1017,7 +1050,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         -> option<O> {
         alt expected {
           some(t) {
-            alt infer::resolve_shallow(fcx.infcx, t, force_none) {
+            alt resolve_type(fcx.infcx, t, force_tvar) {
               result::ok(t) { unpack(ty::get(t).struct) }
               _ { none }
             }
@@ -1119,9 +1152,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
 
             // this will be the call or block that immediately
             // encloses the method call
-            let borrow_scope = fcx.tcx().region_map.get(expr.id);
+            let borrow_lb = fcx.tcx().region_map.get(expr.id);
 
-            let lkup = method::lookup(fcx, expr, base, borrow_scope,
+            let lkup = method::lookup(fcx, expr, base, borrow_lb,
                                       expr.id, field, expr_t, tps,
                                       is_self_ref);
             alt lkup.method() {
@@ -1364,13 +1397,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
       }
       ast::expr_while(cond, body) {
         bot = check_expr_with(fcx, cond, ty::mk_bool(tcx));
-        check_block_no_value(fcx, body);
+        do fcx.with_region_ub(body.node.id) {
+            check_block_no_value(fcx, body);
+        }
         fcx.write_ty(id, ty::mk_nil(tcx));
       }
       ast::expr_loop(body) {
-          check_block_no_value(fcx, body);
-          fcx.write_ty(id, ty::mk_nil(tcx));
-          bot = !may_break(body);
+        do fcx.with_region_ub(body.node.id) {
+            check_block_no_value(fcx, body);
+        }
+        fcx.write_ty(id, ty::mk_nil(tcx));
+        bot = !may_break(body);
       }
       ast::expr_alt(discrim, arms, _) {
         bot = alt::check_alt(fcx, expr, discrim, arms);
@@ -1754,44 +1791,44 @@ fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
       ast::unsafe_blk { @{purity: ast::unsafe_fn with *fcx0} }
       ast::default_blk { fcx0 }
     };
-    vec::push(fcx.blocks, blk.node.id);
-    let mut bot = false;
-    let mut warned = false;
-    for blk.node.stmts.each |s| {
-        if bot && !warned &&
-               alt s.node {
-                 ast::stmt_decl(@{node: ast::decl_local(_), _}, _) |
-                 ast::stmt_expr(_, _) | ast::stmt_semi(_, _) {
-                   true
-                 }
-                 _ { false }
-               } {
-            fcx.ccx.tcx.sess.span_warn(s.span, ~"unreachable statement");
-            warned = true;
+    do fcx.with_region_lb(blk.node.id) {
+        let mut bot = false;
+        let mut warned = false;
+        for blk.node.stmts.each |s| {
+            if bot && !warned &&
+                alt s.node {
+                  ast::stmt_decl(@{node: ast::decl_local(_), _}, _) |
+                  ast::stmt_expr(_, _) | ast::stmt_semi(_, _) {
+                    true
+                  }
+                  _ { false }
+                } {
+                fcx.ccx.tcx.sess.span_warn(s.span, ~"unreachable statement");
+                warned = true;
+            }
+            bot |= check_stmt(fcx, s);
         }
-        bot |= check_stmt(fcx, s);
-    }
-    alt blk.node.expr {
-      none { fcx.write_nil(blk.node.id); }
-      some(e) {
-        if bot && !warned {
-            fcx.ccx.tcx.sess.span_warn(e.span, ~"unreachable expression");
+        alt blk.node.expr {
+          none { fcx.write_nil(blk.node.id); }
+          some(e) {
+            if bot && !warned {
+                fcx.ccx.tcx.sess.span_warn(e.span, ~"unreachable expression");
+            }
+            bot |= check_expr(fcx, e, none);
+            let ety = fcx.expr_ty(e);
+            fcx.write_ty(blk.node.id, ety);
+          }
         }
-        bot |= check_expr(fcx, e, none);
-        let ety = fcx.expr_ty(e);
-        fcx.write_ty(blk.node.id, ety);
-      }
-    }
-    if bot {
-        fcx.write_bot(blk.node.id);
+        if bot {
+            fcx.write_bot(blk.node.id);
+        }
+        bot
     }
-    vec::pop(fcx.blocks);
-    ret bot;
 }
 
 fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
     let rty = ty::node_id_to_type(ccx.tcx, id);
-    let fcx = blank_fn_ctxt(ccx, rty);
+    let fcx = blank_fn_ctxt(ccx, rty, e.id);
     check_expr(fcx, e, none);
     let cty = fcx.expr_ty(e);
     let declty = fcx.ccx.tcx.tcache.get(local_def(id)).ty;
@@ -1817,13 +1854,13 @@ fn check_enum_variants(ccx: @crate_ctxt,
                        vs: ~[ast::variant],
                        id: ast::node_id) {
     let rty = ty::node_id_to_type(ccx.tcx, id);
-    let fcx = blank_fn_ctxt(ccx, rty);
     let mut disr_vals: ~[int] = ~[];
     let mut disr_val = 0;
     let mut variants = ~[];
     for vs.each |v| {
         alt v.node.disr_expr {
           some(e) {
+            let fcx = blank_fn_ctxt(ccx, rty, e.id);
             check_expr(fcx, e, none);
             let cty = fcx.expr_ty(e);
             let declty = ty::mk_int(ccx.tcx);
@@ -2003,7 +2040,7 @@ fn instantiate_path(fcx: @fn_ctxt,
         some(ast_region_to_region(fcx, fcx, sp, r))
       }
       none if tpt.rp => {
-        some(fcx.infcx.next_region_var())
+        some(fcx.infcx.next_region_var_nb())
       }
       none => {
         none
@@ -2037,8 +2074,7 @@ fn instantiate_path(fcx: @fn_ctxt,
 // Resolves `typ` by a single level if `typ` is a type variable.  If no
 // resolution is possible, then an error is reported.
 fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
-    alt infer::resolve_shallow(fcx.infcx, tp,
-                               force_ty_vars_only) {
+    alt infer::resolve_type(fcx.infcx, tp, force_tvar) {
       result::ok(t_s) if !ty::type_is_var(t_s) { ret t_s; }
       _ {
         fcx.ccx.tcx.sess.span_fatal
diff --git a/src/rustc/middle/typeck/check/demand.rs b/src/rustc/middle/typeck/check/demand.rs
index d61aa687511..3718660472b 100644
--- a/src/rustc/middle/typeck/check/demand.rs
+++ b/src/rustc/middle/typeck/check/demand.rs
@@ -26,11 +26,10 @@ fn eqtype(fcx: @fn_ctxt, sp: span,
 }
 
 // Checks that the type `actual` can be assigned to `expected`.
-fn assign(fcx: @fn_ctxt, sp: span, borrow_scope: ast::node_id,
+fn assign(fcx: @fn_ctxt, sp: span, borrow_lb: ast::node_id,
           expected: ty::t, expr: @ast::expr) {
     let expr_ty = fcx.expr_ty(expr);
-    let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
-    alt infer::mk_assignty(fcx.infcx, anmnt, expr_ty, expected) {
+    alt fcx.mk_assignty(expr, borrow_lb, expr_ty, expected) {
       result::ok(()) { /* ok */ }
       result::err(err) {
         fcx.report_mismatched_types(sp, expected, expr_ty, err);
diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs
index 2dc80593e02..5627625daa3 100644
--- a/src/rustc/middle/typeck/check/method.rs
+++ b/src/rustc/middle/typeck/check/method.rs
@@ -19,7 +19,7 @@ class lookup {
     let fcx: @fn_ctxt;
     let expr: @ast::expr;
     let self_expr: @ast::expr;
-    let borrow_scope: ast::node_id;
+    let borrow_lb: ast::node_id;
     let node_id: ast::node_id;
     let m_name: ast::ident;
     let mut self_ty: ty::t;
@@ -32,7 +32,7 @@ class lookup {
     new(fcx: @fn_ctxt,
         expr: @ast::expr,           //expr for a.b in a.b()
         self_expr: @ast::expr,      //a in a.b(...)
-        borrow_scope: ast::node_id, //scope to borrow the expr for
+        borrow_lb: ast::node_id, //scope to borrow the expr for
         node_id: ast::node_id,      //node id where to store type of fn
         m_name: ast::ident,         //b in a.b(...)
         self_ty: ty::t,             //type of a in a.b(...)
@@ -42,7 +42,7 @@ class lookup {
         self.fcx = fcx;
         self.expr = expr;
         self.self_expr = self_expr;
-        self.borrow_scope = borrow_scope;
+        self.borrow_lb = borrow_lb;
         self.node_id = node_id;
         self.m_name = m_name;
         self.self_ty = self_ty;
@@ -315,7 +315,7 @@ class lookup {
                     // type assignability. Collect the matches.
                     let matches = if use_assignability {
                         self.fcx.can_mk_assignty(
-                            self.self_expr, self.borrow_scope,
+                            self.self_expr, self.borrow_lb,
                             self.self_ty, impl_ty)
                     } else {
                         self.fcx.can_mk_subty(self.self_ty, impl_ty)
@@ -377,7 +377,7 @@ class lookup {
         // Make the actual receiver type (cand.self_ty) assignable to the
         // required receiver type (cand.rcvr_ty).  If this method is not
         // from an impl, this'll basically be a no-nop.
-        alt self.fcx.mk_assignty(self.self_expr, self.borrow_scope,
+        alt self.fcx.mk_assignty(self.self_expr, self.borrow_lb,
                                  cand.self_ty, cand.rcvr_ty) {
           result::ok(_) {}
           result::err(_) {
diff --git a/src/rustc/middle/typeck/check/regionck.rs b/src/rustc/middle/typeck/check/regionck.rs
index 9350f577804..e136ccd7b69 100644
--- a/src/rustc/middle/typeck/check/regionck.rs
+++ b/src/rustc/middle/typeck/check/regionck.rs
@@ -8,13 +8,19 @@ know whether a given type will be a region pointer or not until this
 phase.
 
 In particular, we ensure that, if the type of an expression or
-variable is `&r.T`, then the expression or variable must occur within
-the region scope `r`.
+variable is `&r/T`, then the expression or variable must occur within
+the region scope `r`.  Note that in some cases `r` may still be a
+region variable, so this gives us a chance to influence the value for
+`r` that we infer to ensure we choose a value large enough to enclose
+all uses.  There is a lengthy comment in visit_node() that explains
+this point a bit better.
 
 */
 
 import util::ppaux;
 import syntax::print::pprust;
+import infer::{resolve_type, resolve_all, force_all,
+               resolve_rvar, force_rvar};
 
 type rcx = @{fcx: @fn_ctxt, mut errors_reported: uint};
 type rvt = visit::vt<rcx>;
@@ -114,8 +120,31 @@ fn visit_node(id: ast::node_id, span: span, rcx: rcx) -> bool {
     // Try to resolve the type.  If we encounter an error, then typeck
     // is going to fail anyway, so just stop here and let typeck
     // report errors later on in the writeback phase.
+    //
+    // Note one important point: we do not attempt to resolve *region
+    // variables* here.  This is because regionck is essentially adding
+    // constraints to those region variables and so may yet influence
+    // how they are resolved.
+    //
+    // Consider this silly example:
+    //
+    //     fn borrow(x: &int) -> &int {x}
+    //     fn foo(x: @int) -> int {  /* block: B */
+    //         let b = borrow(x);    /* region: <R0> */
+    //         *b
+    //     }
+    //
+    // Here, the region of `b` will be `<R0>`.  `<R0>` is constrainted
+    // to be some subregion of the block B and some superregion of
+    // the call.  If we forced it now, we'd choose the smaller region
+    // (the call).  But that would make the *b illegal.  Since we don't
+    // resolve, the type of b will be `&<R0>.int` and then `*b` will require
+    // that `<R0>` be bigger than the let and the `*b` expression, so we
+    // will effectively resolve `<R0>` to be the block B.
     let ty0 = fcx.node_ty(id);
-    let ty = alt infer::resolve_deep(fcx.infcx, ty0, force_none) {
+    let ty = alt resolve_type(fcx.infcx, ty0,
+                              (resolve_all | force_all) -
+                              (resolve_rvar | force_rvar)) {
       result::err(_) { ret true; }
       result::ok(ty) { ty }
     };
@@ -161,11 +190,12 @@ fn visit_node(id: ast::node_id, span: span, rcx: rcx) -> bool {
 
         alt rcx.fcx.mk_subr(encl_region, region) {
           result::err(_) {
+            let region1 = rcx.fcx.infcx.resolve_region_if_possible(region);
             tcx.sess.span_err(
                 span,
                 #fmt["reference is not valid outside \
                       of its lifetime, %s",
-                     ppaux::region_to_str(tcx, region)]);
+                     ppaux::region_to_str(tcx, region1)]);
             rcx.errors_reported += 1u;
           }
           result::ok(()) {
diff --git a/src/rustc/middle/typeck/check/vtable.rs b/src/rustc/middle/typeck/check/vtable.rs
index a883946252c..a4dd6bae933 100644
--- a/src/rustc/middle/typeck/check/vtable.rs
+++ b/src/rustc/middle/typeck/check/vtable.rs
@@ -1,4 +1,5 @@
 import check::{fn_ctxt, impl_self_ty, methods};
+import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str};
 
 fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
     vec::any(tps, |bs| {
@@ -178,14 +179,14 @@ fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
 
 fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
     let tcx = fcx.ccx.tcx;
-    alt infer::resolve_deep(fcx.infcx, ty, force_all) {
+    alt resolve_type(fcx.infcx, ty, resolve_all | force_all) {
       result::ok(new_type) { new_type }
       result::err(e) {
         tcx.sess.span_fatal(
             sp,
             #fmt["cannot determine a type \
                   for this bounded type parameter: %s",
-                 infer::fixup_err_to_str(e)])
+                 fixup_err_to_str(e)])
       }
     }
 }
diff --git a/src/rustc/middle/typeck/check/writeback.rs b/src/rustc/middle/typeck/check/writeback.rs
index a55c0515d76..720c85bf18e 100644
--- a/src/rustc/middle/typeck/check/writeback.rs
+++ b/src/rustc/middle/typeck/check/writeback.rs
@@ -3,14 +3,14 @@
 // substitutions.
 
 import check::{fn_ctxt, lookup_local, methods};
-
+import infer::{resolve_type, resolve_all, force_all};
 export resolve_type_vars_in_fn;
 export resolve_type_vars_in_expr;
 
 fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) ->
     option<ty::t> {
     if !ty::type_needs_infer(typ) { ret some(typ); }
-    alt infer::resolve_deep(fcx.infcx, typ, force_all) {
+    alt resolve_type(fcx.infcx, typ, resolve_all | force_all) {
       result::ok(new_type) { ret some(new_type); }
       result::err(e) {
         if !fcx.ccx.tcx.sess.has_errors() {
@@ -130,7 +130,8 @@ fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) {
 fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
     if !wbcx.success { ret; }
     let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
-    alt infer::resolve_deep_var(wbcx.fcx.infcx, var_id, force_all) {
+    let var_ty = ty::mk_var(wbcx.fcx.tcx(), var_id);
+    alt resolve_type(wbcx.fcx.infcx, var_ty, resolve_all | force_all) {
       result::ok(lty) {
         #debug["Type for local %s (id %d) resolved to %s",
                pat_to_str(l.node.pat), l.node.id,
@@ -166,6 +167,9 @@ fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool {
     let wbcx = {fcx: fcx, mut success: true};
     let visit = mk_visitor();
     visit.visit_expr(e, wbcx, visit);
+    if wbcx.success {
+        infer::resolve_borrowings(fcx.infcx);
+    }
     ret wbcx.success;
 }
 
@@ -178,5 +182,8 @@ fn resolve_type_vars_in_fn(fcx: @fn_ctxt,
     for decl.inputs.each |arg| {
         resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id);
     }
+    if wbcx.success {
+        infer::resolve_borrowings(fcx.infcx);
+    }
     ret wbcx.success;
 }
diff --git a/src/rustc/middle/typeck/coherence.rs b/src/rustc/middle/typeck/coherence.rs
index 228bf408836..593b7d2a2e1 100644
--- a/src/rustc/middle/typeck/coherence.rs
+++ b/src/rustc/middle/typeck/coherence.rs
@@ -242,7 +242,7 @@ class CoherenceChecker {
     fn universally_quantify_polytype(polytype: ty_param_bounds_and_ty) -> t {
         let self_region =
             if polytype.rp {none}
-            else {some(self.inference_context.next_region_var())};
+            else {some(self.inference_context.next_region_var_nb())};
 
         let bounds_count = polytype.bounds.len();
         let type_parameters =
diff --git a/src/rustc/middle/typeck/infer.rs b/src/rustc/middle/typeck/infer.rs
index 9b725072549..f437aa2ed62 100644
--- a/src/rustc/middle/typeck/infer.rs
+++ b/src/rustc/middle/typeck/infer.rs
@@ -181,6 +181,7 @@ import driver::session::session;
 import util::common::{indent, indenter};
 import ast::{unsafe_fn, impure_fn, pure_fn, extern_fn};
 import ast::{m_const, m_imm, m_mutbl};
+import dvec::{dvec, extensions};
 
 export infer_ctxt;
 export new_infer_ctxt;
@@ -188,16 +189,16 @@ export mk_subty, can_mk_subty;
 export mk_subr;
 export mk_eqty;
 export mk_assignty, can_mk_assignty;
-export resolve_shallow;
-export resolve_deep;
-export resolve_deep_var;
+export resolve_nested_tvar, resolve_rvar, resolve_ivar, resolve_all;
+export force_tvar, force_rvar, force_ivar, force_all;
+export resolve_type, resolve_region;
+export resolve_borrowings;
 export methods; // for infer_ctxt
 export unify_methods; // for infer_ctxt
 export fixup_err, fixup_err_to_str;
 export assignment;
 export root, to_str;
 export int_ty_set_all;
-export force_level, force_none, force_ty_vars_only, force_all;
 
 // Bitvector to represent sets of integral types
 enum int_ty_set = uint;
@@ -280,7 +281,8 @@ fn convert_integral_ty_to_int_ty_set(tcx: ty::ctxt, t: ty::t)
 // value should be borrowed.
 type assignment = {
     expr_id: ast::node_id,
-    borrow_scope: ast::node_id
+    borrow_lb: ast::node_id,
+    borrow_ub: ast::node_id,
 };
 
 type bound<T:copy> = option<T>;
@@ -321,6 +323,10 @@ enum infer_ctxt = @{
     ty_var_counter: @mut uint,
     ty_var_integral_counter: @mut uint,
     region_var_counter: @mut uint,
+
+    borrowings: dvec<{expr_id: ast::node_id,
+                      scope: ty::region,
+                      mutbl: ast::mutability}>
 };
 
 enum fixup_err {
@@ -328,7 +334,7 @@ enum fixup_err {
     unresolved_ty(tv_vid),
     cyclic_ty(tv_vid),
     unresolved_region(region_vid),
-    cyclic_region(region_vid)
+    region_var_bound_by_region_var(region_vid, region_vid)
 }
 
 fn fixup_err_to_str(f: fixup_err) -> ~str {
@@ -337,7 +343,10 @@ fn fixup_err_to_str(f: fixup_err) -> ~str {
       unresolved_ty(_) { ~"unconstrained type" }
       cyclic_ty(_) { ~"cyclic type of infinite size" }
       unresolved_region(_) { ~"unconstrained region" }
-      cyclic_region(_) { ~"cyclic region" }
+      region_var_bound_by_region_var(r1, r2) {
+        #fmt["region var %? bound by another region var %?; this is \
+              a bug in rustc", r1, r2]
+      }
     }
 }
 
@@ -351,7 +360,8 @@ fn new_infer_ctxt(tcx: ty::ctxt) -> infer_ctxt {
                  rb: {vals: smallintmap::mk(), mut bindings: ~[]},
                  ty_var_counter: @mut 0u,
                  ty_var_integral_counter: @mut 0u,
-                 region_var_counter: @mut 0u})}
+                 region_var_counter: @mut 0u,
+                 borrowings: dvec()})}
 
 fn mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
     #debug["mk_subty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
@@ -387,10 +397,11 @@ fn can_mk_assignty(cx: infer_ctxt, anmnt: assignment,
     #debug["can_mk_assignty(%? / %s <: %s)",
            anmnt, a.to_str(cx), b.to_str(cx)];
 
-    // FIXME (#2593): this will not unroll any entries we make in the
-    // borrowings table.  But this is OK for the moment because this is only
-    // used in method lookup, and there must be exactly one match or an
-    // error is reported. Still, it should be fixed.
+    // FIXME(#2593)---this will not unroll any entries we make in the
+    // borrowings table.  But this is OK for the moment because this
+    // is only used in method lookup, and there must be exactly one
+    // match or an error is reported. Still, it should be fixed. (#2593)
+    // NDM OUTDATED
 
     indent(|| cx.probe(||
         cx.assign_tys(anmnt, a, b)
@@ -398,21 +409,32 @@ fn can_mk_assignty(cx: infer_ctxt, anmnt: assignment,
 }
 
 // See comment on the type `resolve_state` below
-fn resolve_shallow(cx: infer_ctxt, a: ty::t,
-                   force_vars: force_level) -> fres<ty::t> {
-    resolver(cx, false, force_vars).resolve(a)
+fn resolve_type(cx: infer_ctxt, a: ty::t, modes: uint)
+    -> fres<ty::t> {
+    resolver(cx, modes).resolve_type_chk(a)
 }
 
-// See comment on the type `resolve_state` below
-fn resolve_deep_var(cx: infer_ctxt, vid: tv_vid,
-                    force_vars: force_level) -> fres<ty::t> {
-    resolver(cx, true, force_vars).resolve(ty::mk_var(cx.tcx, vid))
+fn resolve_region(cx: infer_ctxt, r: ty::region, modes: uint)
+    -> fres<ty::region> {
+    resolver(cx, modes).resolve_region_chk(r)
 }
 
-// See comment on the type `resolve_state` below
-fn resolve_deep(cx: infer_ctxt, a: ty::t, force_vars: force_level)
-    -> fres<ty::t> {
-    resolver(cx, true, force_vars).resolve(a)
+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];
+            cx.tcx.borrowings.insert(
+                item.expr_id, {scope_id: scope_id, mutbl: item.mutbl});
+          }
+
+          r => {
+            cx.tcx.sess.bug(
+                #fmt["borrowing resolved to %?, not a valid scope", r]);
+          }
+        }
+    }
 }
 
 impl methods for ures {
@@ -567,6 +589,8 @@ impl transaction_methods for infer_ctxt {
 
         let tvbl = self.tvb.bindings.len();
         let rbl = self.rb.bindings.len();
+        let bl = self.borrowings.len();
+
         #debug["try(tvbl=%u, rbl=%u)", tvbl, rbl];
         let r <- f();
         alt r {
@@ -575,6 +599,7 @@ impl transaction_methods for infer_ctxt {
             #debug["try--rollback"];
             rollback_to(self.tvb, tvbl);
             rollback_to(self.rb, rbl);
+            while self.borrowings.len() != bl { self.borrowings.pop(); }
           }
         }
         ret r;
@@ -621,16 +646,19 @@ impl methods for infer_ctxt {
         ty::mk_var_integral(self.tcx, self.next_ty_var_integral_id())
     }
 
-    fn next_region_var_id() -> region_vid {
+    fn next_region_var_id(bnds: bounds<ty::region>) -> region_vid {
         let id = *self.region_var_counter;
         *self.region_var_counter += 1u;
-        self.rb.vals.insert(id,
-                            root({lb: none, ub: none}, 0u));
+        self.rb.vals.insert(id, root(bnds, 0));
         ret region_vid(id);
     }
 
-    fn next_region_var() -> ty::region {
-        ty::re_var(self.next_region_var_id())
+    fn next_region_var(bnds: bounds<ty::region>) -> ty::region {
+        ty::re_var(self.next_region_var_id(bnds))
+    }
+
+    fn next_region_var_nb() -> ty::region { // nb == "no bounds"
+        self.next_region_var({lb: none, ub: none})
     }
 
     fn ty_to_str(t: ty::t) -> ~str {
@@ -639,11 +667,18 @@ impl methods for infer_ctxt {
     }
 
     fn resolve_type_vars_if_possible(typ: ty::t) -> ty::t {
-        alt infer::resolve_deep(self, typ, force_none) {
+        alt resolve_type(self, typ, resolve_all) {
           result::ok(new_type) { ret new_type; }
           result::err(_) { ret typ; }
         }
     }
+
+    fn resolve_region_if_possible(oldr: ty::region) -> ty::region {
+        alt resolve_region(self, oldr, resolve_all) {
+          result::ok(newr) { ret newr; }
+          result::err(_) { ret oldr; }
+        }
+    }
 }
 
 impl unify_methods for infer_ctxt {
@@ -1029,67 +1064,70 @@ impl unify_methods for infer_ctxt {
 // the behavior in the face of unconstrained type and region
 // variables.
 
-enum force_level {
-    // Any unconstrained variables are OK.
-    force_none,
-
-    // Unconstrained region vars and integral ty vars are OK;
-    // unconstrained general-purpose ty vars result in an error.
-    force_ty_vars_only,
-
-    // Any unconstrained variables result in an error.
-    force_all,
-}
-
+const resolve_nested_tvar: uint = 0b00000001;
+const resolve_rvar: uint        = 0b00000010;
+const resolve_ivar: uint        = 0b00000100;
+const resolve_all: uint         = 0b00000111;
+const force_tvar: uint          = 0b00010000;
+const force_rvar: uint          = 0b00100000;
+const force_ivar: uint          = 0b01000000;
+const force_all: uint           = 0b01110000;
 
 type resolve_state = @{
     infcx: infer_ctxt,
-    deep: bool,
-    force_vars: force_level,
+    modes: uint,
     mut err: option<fixup_err>,
-    mut r_seen: ~[region_vid],
     mut v_seen: ~[tv_vid]
 };
 
-fn resolver(infcx: infer_ctxt, deep: bool, fvars: force_level)
+fn resolver(infcx: infer_ctxt, modes: uint)
     -> resolve_state {
     @{infcx: infcx,
-      deep: deep,
-      force_vars: fvars,
+      modes: modes,
       mut err: none,
-      mut r_seen: ~[],
       mut v_seen: ~[]}
 }
 
 impl methods for resolve_state {
-    fn resolve(typ: ty::t) -> fres<ty::t> {
+    fn should(mode: uint) -> bool {
+        (self.modes & mode) == mode
+    }
+
+    fn resolve_type_chk(typ: ty::t) -> fres<ty::t> {
         self.err = none;
 
-        #debug["Resolving %s (deep=%b, force_vars=%?)",
+        #debug["Resolving %s (modes=%x)",
                ty_to_str(self.infcx.tcx, typ),
-               self.deep,
-               self.force_vars];
+               self.modes];
 
         // n.b. This is a hokey mess because the current fold doesn't
         // allow us to pass back errors in any useful way.
 
-        assert vec::is_empty(self.v_seen) && vec::is_empty(self.r_seen);
-        let rty = indent(|| self.resolve1(typ) );
-        assert vec::is_empty(self.v_seen) && vec::is_empty(self.r_seen);
+        assert vec::is_empty(self.v_seen);
+        let rty = indent(|| self.resolve_type(typ) );
+        assert vec::is_empty(self.v_seen);
         alt self.err {
           none {
-            #debug["Resolved to %s (deep=%b, force_vars=%?)",
+            #debug["Resolved to %s (modes=%x)",
                    ty_to_str(self.infcx.tcx, rty),
-                   self.deep,
-                   self.force_vars];
+                   self.modes];
             ret ok(rty);
           }
           some(e) { ret err(e); }
         }
     }
 
-    fn resolve1(typ: ty::t) -> ty::t {
-        #debug("Resolve1(%s)", typ.to_str(self.infcx));
+    fn resolve_region_chk(orig: ty::region) -> fres<ty::region> {
+        self.err = none;
+        let resolved = indent(|| self.resolve_region(orig) );
+        alt self.err {
+          none {ok(resolved)}
+          some(e) {err(e)}
+        }
+    }
+
+    fn resolve_type(typ: ty::t) -> ty::t {
+        #debug("resolve_type(%s)", typ.to_str(self.infcx));
         indent(fn&() -> ty::t {
             if !ty::type_needs_infer(typ) { ret typ; }
 
@@ -1100,23 +1138,30 @@ impl methods for resolve_state {
               ty::ty_var_integral(vid) {
                 self.resolve_ty_var_integral(vid)
               }
-              _ if !ty::type_has_regions(typ) && !self.deep {
-                typ
-              }
               _ {
-                ty::fold_regions_and_ty(
-                    self.infcx.tcx, typ,
-                    |r| self.resolve_region(r),
-                    |t| self.resolve_if_deep(t),
-                    |t| self.resolve_if_deep(t))
+                if !self.should(resolve_rvar) &&
+                    !self.should(resolve_nested_tvar) {
+                    // shortcircuit for efficiency
+                    typ
+                } else {
+                    ty::fold_regions_and_ty(
+                        self.infcx.tcx, typ,
+                        |r| self.resolve_region(r),
+                        |t| self.resolve_nested_tvar(t),
+                        |t| self.resolve_nested_tvar(t))
+                }
               }
             }
         })
     }
 
-    fn resolve_if_deep(typ: ty::t) -> ty::t {
+    fn resolve_nested_tvar(typ: ty::t) -> ty::t {
         #debug("Resolve_if_deep(%s)", typ.to_str(self.infcx));
-        if !self.deep {typ} else {self.resolve1(typ)}
+        if !self.should(resolve_nested_tvar) {
+            typ
+        } else {
+            self.resolve_type(typ)
+        }
     }
 
     fn resolve_region(orig: ty::region) -> ty::region {
@@ -1128,29 +1173,29 @@ impl methods for resolve_state {
     }
 
     fn resolve_region_var(rid: region_vid) -> ty::region {
-        if vec::contains(self.r_seen, rid) {
-            self.err = some(cyclic_region(rid));
-            ret ty::re_var(rid);
-        } else {
-            vec::push(self.r_seen, rid);
-            let nde = self.infcx.get(self.infcx.rb, rid);
-            let bounds = nde.possible_types;
+        if !self.should(resolve_rvar) {
+            ret ty::re_var(rid)
+        }
+        let nde = self.infcx.get(self.infcx.rb, rid);
+        let bounds = nde.possible_types;
+        alt bounds {
+          { ub:_, lb:some(r) } => { self.assert_not_rvar(rid, r); r }
+          { ub:some(r), lb:_ } => { self.assert_not_rvar(rid, r); r }
+          { ub:none, lb:none } => {
+            if self.should(force_rvar) {
+                self.err = some(unresolved_region(rid));
+            }
+            ty::re_var(rid)
+          }
+        }
+    }
 
-            let r1 = alt bounds {
-              { ub:_, lb:some(t) } { self.resolve_region(t) }
-              { ub:some(t), lb:_ } { self.resolve_region(t) }
-              { ub:none, lb:none } {
-                alt self.force_vars {
-                  force_all {
-                    self.err = some(unresolved_region(rid));
-                  }
-                  _ { /* ok */ }
-                }
-                ty::re_var(rid)
-              }
-            };
-            vec::pop(self.r_seen);
-            ret r1;
+    fn assert_not_rvar(rid: region_vid, r: ty::region) {
+        alt r {
+          ty::re_var(rid2) => {
+            self.err = some(region_var_bound_by_region_var(rid, rid2));
+          }
+          _ => { }
         }
     }
 
@@ -1172,15 +1217,12 @@ impl methods for resolve_state {
             let bounds = nde.possible_types;
 
             let t1 = alt bounds {
-              { ub:_, lb:some(t) } if !type_is_bot(t) { self.resolve1(t) }
-              { ub:some(t), lb:_ } { self.resolve1(t) }
-              { ub:_, lb:some(t) } { self.resolve1(t) }
+              { ub:_, lb:some(t) } if !type_is_bot(t) { self.resolve_type(t) }
+              { ub:some(t), lb:_ } { self.resolve_type(t) }
+              { ub:_, lb:some(t) } { self.resolve_type(t) }
               { ub:none, lb:none } {
-                alt self.force_vars {
-                  force_ty_vars_only | force_all {
+                if self.should(force_tvar) {
                     self.err = some(unresolved_ty(vid));
-                  }
-                  force_none { /* ok */ }
                 }
                 ty::mk_var(tcx, vid)
               }
@@ -1191,6 +1233,10 @@ impl methods for resolve_state {
     }
 
     fn resolve_ty_var_integral(vid: tvi_vid) -> ty::t {
+        if !self.should(resolve_ivar) {
+            ret ty::mk_var_integral(self.infcx.tcx, vid);
+        }
+
         let nde = self.infcx.get(self.infcx.tvib, vid);
         let pt = nde.possible_types;
 
@@ -1199,8 +1245,7 @@ impl methods for resolve_state {
         alt single_type_contained_in(self.infcx.tcx, pt) {
           some(t) { t }
           none {
-            alt self.force_vars {
-              force_all {
+            if self.should(force_ivar) {
                 // As a last resort, default to int.
                 let ty = ty::mk_int(self.infcx.tcx);
                 self.infcx.set(
@@ -1209,10 +1254,8 @@ impl methods for resolve_state {
                                                            ty),
                         nde.rank));
                 ty
-              }
-              force_none | force_ty_vars_only {
+            } else {
                 ty::mk_var_integral(self.infcx.tcx, vid)
-              }
             }
           }
         }
@@ -1393,15 +1436,22 @@ impl assignment for infer_ctxt {
 
         do indent {
             do self.sub_tys(a, nr_b).then {
-                let r_a = ty::re_scope(anmnt.borrow_scope);
+                // 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)});
+
                 #debug["anmnt=%?", anmnt];
                 do sub(self).contraregions(r_a, r_b).chain |_r| {
                     // if successful, add an entry indicating that
                     // borrowing occurred
-                    #debug["borrowing expression #%?", anmnt];
-                    let borrow = {scope_id: anmnt.borrow_scope,
-                                  mutbl: m};
-                    self.tcx.borrowings.insert(anmnt.expr_id, borrow);
+                    #debug["borrowing expression #%?, scope=%?, m=%?",
+                           anmnt, r_a, m];
+                    self.borrowings.push({expr_id: anmnt.expr_id,
+                                          scope: r_a,
+                                          mutbl: m});
                     uok()
                 }
             }
@@ -1921,7 +1971,7 @@ impl of combine for sub {
                 // anything to do with the region variable that's created
                 // for it.  The only thing we're doing with `br` here is
                 // using it in the debug message.
-                let rvar = self.infcx().next_region_var();
+                let rvar = self.infcx().next_region_var_nb();
                 #debug["Bound region %s maps to %s",
                        bound_region_to_str(self.tcx, br),
                        region_to_str(self.tcx, rvar)];
diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs
index 04152eb69d3..cbcd4b31e53 100644
--- a/src/rustc/util/ppaux.rs
+++ b/src/rustc/util/ppaux.rs
@@ -43,7 +43,11 @@ fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> ~str {
             #fmt("<alt at %s>",
                  codemap::span_to_str(expr.span, cx.sess.codemap))
           }
-          ast::expr_field(*) | ast::expr_unary(*) | ast::expr_binary(*) {
+          ast::expr_assign_op(*) |
+          ast::expr_field(*) |
+          ast::expr_unary(*) |
+          ast::expr_binary(*) |
+          ast::expr_index(*) {
             #fmt("<method at %s>",
                  codemap::span_to_str(expr.span, cx.sess.codemap))
           }
diff --git a/src/test/compile-fail/regions-borrow.rs b/src/test/compile-fail/regions-borrow.rs
deleted file mode 100644
index 5e893246cdc..00000000000
--- a/src/test/compile-fail/regions-borrow.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-fn foo(x: &uint) -> &uint { x }
-
-fn main() {
-    let p = @3u;
-    let r = foo(p);
-    //~^ ERROR reference is not valid
-    assert *p == *r;
-}
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
new file mode 100644
index 00000000000..aeb864d92a7
--- /dev/null
+++ b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
@@ -0,0 +1,14 @@
+type point = {x: int, y: int};
+
+fn x_coord(p: &point) -> &int {
+    ret &p.x;
+}
+
+fn foo(p: @point) -> &int {
+    let xc = x_coord(p);
+    assert *xc == 3;
+    ret xc; //~ ERROR mismatched types: expected `&int` but found
+}
+
+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
new file mode 100644
index 00000000000..e89d801b361
--- /dev/null
+++ b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
@@ -0,0 +1,11 @@
+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);
+        assert *x == *y;
+    }
+    assert *x == *y;
+}
diff --git a/src/test/compile-fail/regions-escape-into-other-fn.rs b/src/test/run-pass/regions-escape-into-other-fn.rs
index 67c7dd3317a..8f71d9821fe 100644
--- a/src/test/compile-fail/regions-escape-into-other-fn.rs
+++ b/src/test/run-pass/regions-escape-into-other-fn.rs
@@ -3,5 +3,5 @@ fn bar(x: &uint) -> uint { *x }
 
 fn main() {
     let p = @3u;
-    bar(foo(p)); //~ ERROR reference is not valid
+    assert bar(foo(p)) == 3;
 }
diff --git a/src/test/run-pass/regions-infer-borrow-scope-view.rs b/src/test/run-pass/regions-infer-borrow-scope-view.rs
new file mode 100644
index 00000000000..4f3b668269a
--- /dev/null
+++ b/src/test/run-pass/regions-infer-borrow-scope-view.rs
@@ -0,0 +1,9 @@
+fn view<T>(x: &[T]) -> &[T] {x}
+
+fn main() {
+    let v = ~[1, 2, 3];
+    let x = view(v);
+    let y = view(x);
+    assert (v[0] == x[0]) && (v[0] == y[0]);
+}
+
diff --git a/src/test/run-pass/regions-infer-borrow-scope-within-loop-ok.rs b/src/test/run-pass/regions-infer-borrow-scope-within-loop-ok.rs
new file mode 100644
index 00000000000..bd51c4beca5
--- /dev/null
+++ b/src/test/run-pass/regions-infer-borrow-scope-within-loop-ok.rs
@@ -0,0 +1,10 @@
+fn borrow<T>(x: &T) -> &T {x}
+
+fn main() {
+    let x = @3;
+    loop {
+        let y = borrow(x);
+        assert *x == *y;
+	break;
+    }
+}
diff --git a/src/test/run-pass/regions-infer-borrow-scope.rs b/src/test/run-pass/regions-infer-borrow-scope.rs
new file mode 100644
index 00000000000..0f099672f40
--- /dev/null
+++ b/src/test/run-pass/regions-infer-borrow-scope.rs
@@ -0,0 +1,12 @@
+type point = {x: int, y: int};
+
+fn x_coord(p: &point) -> &int {
+    ret &p.x;
+}
+
+fn main() {
+    let p = @{x: 3, y: 4};
+    let xc = x_coord(p);
+    assert *xc == 3;
+}
+