about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-01-03 14:20:47 +0000
committerbors <bors@rust-lang.org>2015-01-03 14:20:47 +0000
commitfe7e285d0cbae8e44c3853a6965caf2b575dcfcc (patch)
treeb373f2a7540f38f2e2e70a5fd194839e2ddf576d /src
parentfc2ba13939aa9672d886beb06efde7aeda2d5f7f (diff)
parentcbeff8b8b3bd28e99a3a034a41c8bdcd830fa884 (diff)
downloadrust-fe7e285d0cbae8e44c3853a6965caf2b575dcfcc.tar.gz
rust-fe7e285d0cbae8e44c3853a6965caf2b575dcfcc.zip
auto merge of #20432 : nikomatsakis/rust/fn-inference-2, r=eddyb
Previously, the borrow mode of each upvar was inferred as part of regionck. This PR moves it into its own separate step. It also employs the `ExprUseVisitor`, further simplifying the code. The eventual goal is to support better inference of `Fn` vs `FnMut` vs `FnOnce` that is not based on the expected type, as well as supporting individual by-move upvars.

r? @eddyb 
Diffstat (limited to 'src')
-rw-r--r--src/librustc/lint/builtin.rs10
-rw-r--r--src/librustc/middle/check_match.rs11
-rw-r--r--src/librustc/middle/check_rvalues.rs6
-rw-r--r--src/librustc/middle/check_static.rs10
-rw-r--r--src/librustc/middle/def.rs6
-rw-r--r--src/librustc/middle/expr_use_visitor.rs171
-rw-r--r--src/librustc/middle/intrinsicck.rs14
-rw-r--r--src/librustc/middle/mem_categorization.rs365
-rw-r--r--src/librustc/middle/traits/coherence.rs4
-rw-r--r--src/librustc/middle/traits/error_reporting.rs8
-rw-r--r--src/librustc/middle/traits/fulfill.rs12
-rw-r--r--src/librustc/middle/traits/mod.rs91
-rw-r--r--src/librustc/middle/traits/select.rs23
-rw-r--r--src/librustc/middle/ty.rs178
-rw-r--r--src/librustc_borrowck/borrowck/check_loans.rs6
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/mod.rs10
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs70
-rw-r--r--src/librustc_trans/trans/_match.rs14
-rw-r--r--src/librustc_trans/trans/adt.rs5
-rw-r--r--src/librustc_trans/trans/base.rs4
-rw-r--r--src/librustc_trans/trans/common.rs52
-rw-r--r--src/librustc_trans/trans/datum.rs6
-rw-r--r--src/librustc_trans/trans/monomorphize.rs7
-rw-r--r--src/librustc_typeck/check/assoc.rs3
-rw-r--r--src/librustc_typeck/check/method/mod.rs4
-rw-r--r--src/librustc_typeck/check/method/probe.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs61
-rw-r--r--src/librustc_typeck/check/regionck.rs374
-rw-r--r--src/librustc_typeck/check/upvar.rs373
-rw-r--r--src/librustc_typeck/check/vtable.rs8
-rw-r--r--src/librustc_typeck/coherence/mod.rs2
-rw-r--r--src/test/compile-fail/borrowck-closures-mut-of-imm.rs1
-rw-r--r--src/test/compile-fail/issue-17551.rs4
-rw-r--r--src/test/compile-fail/privacy1.rs3
-rw-r--r--src/test/compile-fail/privacy4.rs1
35 files changed, 1064 insertions, 857 deletions
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index c314fba91d5..53249c72462 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -1612,15 +1612,11 @@ impl LintPass for MissingCopyImplementations {
             }
             _ => return,
         };
-        let parameter_environment = ty::empty_parameter_environment();
-        if !ty::type_moves_by_default(cx.tcx,
-                                      ty,
-                                      &parameter_environment) {
+        let parameter_environment = ty::empty_parameter_environment(cx.tcx);
+        if !ty::type_moves_by_default(&parameter_environment, item.span, ty) {
             return
         }
-        if ty::can_type_implement_copy(cx.tcx,
-                                       ty,
-                                       &parameter_environment).is_ok() {
+        if ty::can_type_implement_copy(&parameter_environment, item.span, ty).is_ok() {
             cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
                          item.span,
                          "type could implement `Copy`; consider adding `impl \
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index d16224ec5b8..9cb8674c3e1 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -99,7 +99,7 @@ impl<'a> FromIterator<Vec<&'a Pat>> for Matrix<'a> {
 
 pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
     pub tcx: &'a ty::ctxt<'tcx>,
-    pub param_env: ParameterEnvironment<'tcx>,
+    pub param_env: ParameterEnvironment<'a, 'tcx>,
 }
 
 #[deriving(Clone, PartialEq)]
@@ -148,7 +148,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
 pub fn check_crate(tcx: &ty::ctxt) {
     visit::walk_crate(&mut MatchCheckCtxt {
         tcx: tcx,
-        param_env: ty::empty_parameter_environment(),
+        param_env: ty::empty_parameter_environment(tcx),
     }, tcx.map.krate());
     tcx.sess.abort_if_errors();
 }
@@ -1032,9 +1032,7 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
                 match p.node {
                     ast::PatIdent(ast::BindByValue(_), _, ref sub) => {
                         let pat_ty = ty::node_id_to_type(tcx, p.id);
-                        if ty::type_moves_by_default(tcx,
-                                                      pat_ty,
-                                                      &cx.param_env) {
+                        if ty::type_moves_by_default(&cx.param_env, pat.span, pat_ty) {
                             check_move(p, sub.as_ref().map(|p| &**p));
                         }
                     }
@@ -1063,8 +1061,7 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>,
         cx: cx,
     };
     let mut visitor = ExprUseVisitor::new(&mut checker,
-                                          checker.cx.tcx,
-                                          &cx.param_env);
+                                          &checker.cx.param_env);
     visitor.walk_expr(guard);
 }
 
diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs
index 3b4ea5234f4..c383b1579ef 100644
--- a/src/librustc/middle/check_rvalues.rs
+++ b/src/librustc/middle/check_rvalues.rs
@@ -41,7 +41,7 @@ impl<'a, 'tcx, 'v> visit::Visitor<'v> for RvalueContext<'a, 'tcx> {
         {
             let param_env = ParameterEnvironment::for_item(self.tcx, fn_id);
             let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: &param_env };
-            let mut euv = euv::ExprUseVisitor::new(&mut delegate, self.tcx, &param_env);
+            let mut euv = euv::ExprUseVisitor::new(&mut delegate, &param_env);
             euv.walk_fn(fd, b);
         }
         visit::walk_fn(self, fk, fd, b, s)
@@ -50,7 +50,7 @@ impl<'a, 'tcx, 'v> visit::Visitor<'v> for RvalueContext<'a, 'tcx> {
 
 struct RvalueContextDelegate<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
-    param_env: &'a ty::ParameterEnvironment<'tcx>,
+    param_env: &'a ty::ParameterEnvironment<'a,'tcx>,
 }
 
 impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContextDelegate<'a, 'tcx> {
@@ -60,7 +60,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContextDelegate<'a, 'tcx> {
                cmt: mc::cmt<'tcx>,
                _: euv::ConsumeMode) {
         debug!("consume; cmt: {}; type: {}", *cmt, ty_to_string(self.tcx, cmt.ty));
-        if !ty::type_is_sized(self.tcx, cmt.ty, self.param_env) {
+        if !ty::type_is_sized(self.param_env, span, cmt.ty) {
             span_err!(self.tcx.sess, span, E0161,
                 "cannot move a value of type {0}: the size of {0} cannot be statically determined",
                 ty_to_string(self.tcx, cmt.ty));
diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs
index fb20df020ac..9c9e68002c9 100644
--- a/src/librustc/middle/check_static.rs
+++ b/src/librustc/middle/check_static.rs
@@ -54,7 +54,7 @@ struct CheckStaticVisitor<'a, 'tcx: 'a> {
 }
 
 struct GlobalVisitor<'a,'b,'tcx:'a+'b>(
-    euv::ExprUseVisitor<'a,'b,'tcx,ty::ctxt<'tcx>>);
+    euv::ExprUseVisitor<'a,'b,'tcx,ty::ParameterEnvironment<'b,'tcx>>);
 struct GlobalChecker {
     static_consumptions: NodeSet,
     const_borrows: NodeSet,
@@ -70,8 +70,8 @@ pub fn check_crate(tcx: &ty::ctxt) {
         static_local_borrows: NodeSet::new(),
     };
     {
-        let param_env = ty::empty_parameter_environment();
-        let visitor = euv::ExprUseVisitor::new(&mut checker, tcx, &param_env);
+        let param_env = ty::empty_parameter_environment(tcx);
+        let visitor = euv::ExprUseVisitor::new(&mut checker, &param_env);
         visit::walk_crate(&mut GlobalVisitor(visitor), tcx.map.krate());
     }
     visit::walk_crate(&mut CheckStaticVisitor {
@@ -121,8 +121,8 @@ impl<'a, 'tcx> CheckStaticVisitor<'a, 'tcx> {
         let mut fulfill_cx = traits::FulfillmentContext::new();
         let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
         fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
-        let env = ty::empty_parameter_environment();
-        match fulfill_cx.select_all_or_error(&infcx, &env, self.tcx) {
+        let env = ty::empty_parameter_environment(self.tcx);
+        match fulfill_cx.select_all_or_error(&infcx, &env) {
             Ok(()) => { },
             Err(ref errors) => {
                 traits::report_fulfillment_errors(&infcx, errors);
diff --git a/src/librustc/middle/def.rs b/src/librustc/middle/def.rs
index ff1ee394966..acbb7d567dc 100644
--- a/src/librustc/middle/def.rs
+++ b/src/librustc/middle/def.rs
@@ -126,6 +126,12 @@ impl TraitItemKind {
 }
 
 impl Def {
+    pub fn local_node_id(&self) -> ast::NodeId {
+        let def_id = self.def_id();
+        assert_eq!(def_id.krate, ast::LOCAL_CRATE);
+        def_id.node
+    }
+
     pub fn def_id(&self) -> ast::DefId {
         match *self {
             DefFn(id, _) | DefStaticMethod(id, _) | DefMod(id) |
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index d36c85342ce..f5cf4af1230 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -23,7 +23,7 @@ use self::OverloadedCallType::*;
 use middle::{def, region, pat_util};
 use middle::mem_categorization as mc;
 use middle::mem_categorization::Typer;
-use middle::ty::{mod, ParameterEnvironment, Ty};
+use middle::ty::{mod};
 use middle::ty::{MethodCall, MethodObject, MethodTraitObject};
 use middle::ty::{MethodOrigin, MethodParam, MethodTypeParam};
 use middle::ty::{MethodStatic, MethodStaticUnboxedClosure};
@@ -299,7 +299,22 @@ pub struct ExprUseVisitor<'d,'t,'tcx:'t,TYPER:'t> {
     typer: &'t TYPER,
     mc: mc::MemCategorizationContext<'t,TYPER>,
     delegate: &'d mut (Delegate<'tcx>+'d),
-    param_env: &'t ParameterEnvironment<'tcx>,
+}
+
+// If the TYPER results in an error, it's because the type check
+// failed (or will fail, when the error is uncovered and reported
+// during writeback). In this case, we just ignore this part of the
+// code.
+//
+// Note that this macro appears similar to try!(), but, unlike try!(),
+// it does not propagate the error.
+macro_rules! return_if_err {
+    ($inp: expr) => (
+        match $inp {
+            Ok(v) => v,
+            Err(()) => return
+        }
+    )
 }
 
 /// Whether the elements of an overloaded operation are passed by value or by reference
@@ -310,14 +325,12 @@ enum PassArgs {
 
 impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
     pub fn new(delegate: &'d mut Delegate<'tcx>,
-               typer: &'t TYPER,
-               param_env: &'t ParameterEnvironment<'tcx>)
+               typer: &'t TYPER)
                -> ExprUseVisitor<'d,'t,'tcx,TYPER> {
         ExprUseVisitor {
             typer: typer,
             mc: mc::MemCategorizationContext::new(typer),
             delegate: delegate,
-            param_env: param_env,
         }
     }
 
@@ -332,7 +345,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                          decl: &ast::FnDecl,
                          body: &ast::Block) {
         for arg in decl.inputs.iter() {
-            let arg_ty = self.typer.node_ty(arg.pat.id);
+            let arg_ty = return_if_err!(self.typer.node_ty(arg.pat.id));
 
             let fn_body_scope = region::CodeExtent::from_node_id(body.id);
             let arg_cmt = self.mc.cat_rvalue(
@@ -353,10 +366,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                         consume_id: ast::NodeId,
                         consume_span: Span,
                         cmt: mc::cmt<'tcx>) {
-        let mode = copy_or_move(self.tcx(),
-                                cmt.ty,
-                                self.param_env,
-                                DirectRefMove);
+        let mode = copy_or_move(self.typer, &cmt, DirectRefMove);
         self.delegate.consume(consume_id, consume_span, cmt, mode);
     }
 
@@ -369,7 +379,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
     pub fn consume_expr(&mut self, expr: &ast::Expr) {
         debug!("consume_expr(expr={})", expr.repr(self.tcx()));
 
-        let cmt = self.mc.cat_expr(expr);
+        let cmt = return_if_err!(self.mc.cat_expr(expr));
         self.delegate_consume(expr.id, expr.span, cmt);
         self.walk_expr(expr);
     }
@@ -378,7 +388,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                    assignment_expr: &ast::Expr,
                    expr: &ast::Expr,
                    mode: MutateMode) {
-        let cmt = self.mc.cat_expr(expr);
+        let cmt = return_if_err!(self.mc.cat_expr(expr));
         self.delegate.mutate(assignment_expr.id, assignment_expr.span, cmt, mode);
         self.walk_expr(expr);
     }
@@ -391,7 +401,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
         debug!("borrow_expr(expr={}, r={}, bk={})",
                expr.repr(self.tcx()), r.repr(self.tcx()), bk.repr(self.tcx()));
 
-        let cmt = self.mc.cat_expr(expr);
+        let cmt = return_if_err!(self.mc.cat_expr(expr));
         self.delegate.borrow(expr.id, expr.span, cmt, r, bk, cause);
 
         // Note: Unlike consume, we can ignore ExprParen. cat_expr
@@ -491,7 +501,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
             }
 
             ast::ExprMatch(ref discr, ref arms, _) => {
-                let discr_cmt = self.mc.cat_expr(&**discr);
+                let discr_cmt = return_if_err!(self.mc.cat_expr(&**discr));
                 self.borrow_expr(&**discr, ty::ReEmpty, ty::ImmBorrow, MatchDiscriminant);
 
                 // treatment of the discriminant is handled while walking the arms.
@@ -509,7 +519,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
             ast::ExprAddrOf(m, ref base) => {   // &base
                 // make sure that the thing we are pointing out stays valid
                 // for the lifetime `scope_r` of the resulting ptr:
-                let expr_ty = ty::expr_ty(self.tcx(), expr);
+                let expr_ty = return_if_err!(self.typer.node_ty(expr.id));
                 let r = ty::ty_region(self.tcx(), expr.span, expr_ty);
                 let bk = ty::BorrowKind::from_mutbl(m);
                 self.borrow_expr(&**base, r, bk, AddrOf);
@@ -550,7 +560,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
 
                 // Fetch the type of the value that the iteration yields to
                 // produce the pattern's categorized mutable type.
-                let pattern_type = self.typer.node_ty(pat.id);
+                let pattern_type = return_if_err!(self.typer.node_ty(pat.id));
                 let blk_scope = region::CodeExtent::from_node_id(blk.id);
                 let pat_cmt = self.mc.cat_rvalue(pat.id,
                                                  pat.span,
@@ -638,7 +648,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
     }
 
     fn walk_callee(&mut self, call: &ast::Expr, callee: &ast::Expr) {
-        let callee_ty = self.typer.expr_ty_adjusted(callee);
+        let callee_ty = return_if_err!(self.typer.expr_ty_adjusted(callee));
         debug!("walk_callee: callee={} callee_ty={}",
                callee.repr(self.tcx()), callee_ty.repr(self.tcx()));
         let call_scope = region::CodeExtent::from_node_id(call.id);
@@ -659,6 +669,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                     }
                 }
             }
+            ty::ty_err => { }
             _ => {
                 let overloaded_call_type =
                     match self.typer.node_method_origin(MethodCall::expr(call.id)) {
@@ -735,7 +746,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                 // "assigns", which is handled by
                 // `walk_pat`:
                 self.walk_expr(&**expr);
-                let init_cmt = self.mc.cat_expr(&**expr);
+                let init_cmt = return_if_err!(self.mc.cat_expr(&**expr));
                 self.walk_irrefutable_pat(init_cmt, &*local.pat);
             }
         }
@@ -769,7 +780,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
             None => { return; }
         };
 
-        let with_cmt = self.mc.cat_expr(&*with_expr);
+        let with_cmt = return_if_err!(self.mc.cat_expr(&*with_expr));
 
         // Select just those fields of the `with`
         // expression that will actually be used
@@ -778,9 +789,17 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                 ty::struct_fields(self.tcx(), did, substs)
             }
             _ => {
-                self.tcx().sess.span_bug(
-                    with_expr.span,
-                    "with expression doesn't evaluate to a struct");
+                // the base expression should always evaluate to a
+                // struct; however, when EUV is run during typeck, it
+                // may not. This will generate an error earlier in typeck,
+                // so we can just ignore it.
+                if !self.tcx().sess.has_errors() {
+                    self.tcx().sess.span_bug(
+                        with_expr.span,
+                        "with expression doesn't evaluate to a struct");
+                }
+                assert!(self.tcx().sess.has_errors());
+                vec!()
             }
         };
 
@@ -824,7 +843,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                         // rvalue.
                         debug!("walk_adjustment(AutoAddEnv|AdjustReifyFnPointer)");
                         let cmt_unadjusted =
-                            self.mc.cat_expr_unadjusted(expr);
+                            return_if_err!(self.mc.cat_expr_unadjusted(expr));
                         self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
                     }
                     ty::AdjustDerefRef(ty::AutoDerefRef {
@@ -858,7 +877,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
             match self.typer.node_method_ty(deref_id) {
                 None => {}
                 Some(method_ty) => {
-                    let cmt = self.mc.cat_expr_autoderefd(expr, i);
+                    let cmt = return_if_err!(self.mc.cat_expr_autoderefd(expr, i));
                     let self_ty = ty::ty_fn_args(method_ty)[0];
                     let (m, r) = match self_ty.sty {
                         ty::ty_rptr(r, ref m) => (m.mutbl, r),
@@ -888,14 +907,15 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                 assert!(n == 1, format!("Expected exactly 1 deref with Uniq \
                                          AutoRefs, found: {}", n));
                 let cmt_unadjusted =
-                    self.mc.cat_expr_unadjusted(expr);
+                    return_if_err!(self.mc.cat_expr_unadjusted(expr));
                 self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
                 return;
             }
             _ => {}
         }
 
-        let cmt_derefd = self.mc.cat_expr_autoderefd(expr, n);
+        let cmt_derefd = return_if_err!(
+            self.mc.cat_expr_autoderefd(expr, n));
         debug!("walk_adjustment: cmt_derefd={}",
                cmt_derefd.repr(self.tcx()));
 
@@ -988,18 +1008,15 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                                mode: &mut TrackMatchMode<Span>) {
         debug!("determine_pat_move_mode cmt_discr={} pat={}", cmt_discr.repr(self.tcx()),
                pat.repr(self.tcx()));
-        self.mc.cat_pattern(cmt_discr, pat, |_mc, cmt_pat, pat| {
-            let tcx = self.typer.tcx();
-            let def_map = &self.typer.tcx().def_map;
+        return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |_mc, cmt_pat, pat| {
+            let tcx = self.tcx();
+            let def_map = &self.tcx().def_map;
             if pat_util::pat_is_binding(def_map, pat) {
                 match pat.node {
                     ast::PatIdent(ast::BindByRef(_), _, _) =>
                         mode.lub(BorrowingMatch),
                     ast::PatIdent(ast::BindByValue(_), _, _) => {
-                        match copy_or_move(tcx,
-                                           cmt_pat.ty,
-                                           self.param_env,
-                                           PatBindingMove) {
+                        match copy_or_move(self.typer, &cmt_pat, PatBindingMove) {
                             Copy => mode.lub(CopyingMatch),
                             Move(_) => mode.lub(MovingMatch),
                         }
@@ -1011,7 +1028,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                     }
                 }
             }
-        });
+        }));
     }
 
     /// The core driver for walking a pattern; `match_mode` must be
@@ -1026,10 +1043,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
 
         let mc = &self.mc;
         let typer = self.typer;
-        let def_map = &self.typer.tcx().def_map;
+        let def_map = &self.tcx().def_map;
         let delegate = &mut self.delegate;
-        let param_env = self.param_env;
-        mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| {
+        return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| {
             if pat_util::pat_is_binding(def_map, pat) {
                 let tcx = typer.tcx();
 
@@ -1039,13 +1055,17 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                        match_mode);
 
                 // pat_ty: the type of the binding being produced.
-                let pat_ty = typer.node_ty(pat.id);
+                let pat_ty = return_if_err!(typer.node_ty(pat.id));
 
                 // Each match binding is effectively an assignment to the
                 // binding being produced.
                 let def = def_map.borrow()[pat.id].clone();
-                let binding_cmt = mc.cat_def(pat.id, pat.span, pat_ty, def);
-                delegate.mutate(pat.id, pat.span, binding_cmt, Init);
+                match mc.cat_def(pat.id, pat.span, pat_ty, def) {
+                    Ok(binding_cmt) => {
+                        delegate.mutate(pat.id, pat.span, binding_cmt, Init);
+                    }
+                    Err(_) => { }
+                }
 
                 // It is also a borrow or copy/move of the value being matched.
                 match pat.node {
@@ -1058,15 +1078,12 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                                              r, bk, RefBinding);
                     }
                     ast::PatIdent(ast::BindByValue(_), _, _) => {
-                        let mode = copy_or_move(typer.tcx(),
-                                                cmt_pat.ty,
-                                                param_env,
-                                                PatBindingMove);
+                        let mode = copy_or_move(typer, &cmt_pat, PatBindingMove);
                         debug!("walk_pat binding consuming pat");
                         delegate.consume_pat(pat, cmt_pat, mode);
                     }
                     _ => {
-                        typer.tcx().sess.span_bug(
+                        tcx.sess.span_bug(
                             pat.span,
                             "binding pattern not an identifier");
                     }
@@ -1080,7 +1097,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                         // matched.
 
                         let (slice_cmt, slice_mutbl, slice_r) =
-                            mc.cat_slice_pattern(cmt_pat, &**slice_pat);
+                            return_if_err!(mc.cat_slice_pattern(cmt_pat, &**slice_pat));
 
                         // Note: We declare here that the borrow
                         // occurs upon entering the `[...]`
@@ -1110,13 +1127,13 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                     _ => { }
                 }
             }
-        });
+        }));
 
         // Do a second pass over the pattern, calling `matched_pat` on
         // the interior nodes (enum variants and structs), as opposed
         // to the above loop's visit of than the bindings that form
         // the leaves of the pattern tree structure.
-        mc.cat_pattern(cmt_discr, pat, |mc, cmt_pat, pat| {
+        return_if_err!(mc.cat_pattern(cmt_discr, pat, |mc, cmt_pat, pat| {
             let def_map = def_map.borrow();
             let tcx = typer.tcx();
 
@@ -1167,17 +1184,29 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                             // An enum's type -- should never be in a
                             // pattern.
 
-                            let msg = format!("Pattern has unexpected type: {}", def);
-                            tcx.sess.span_bug(pat.span, msg[])
+                            if !tcx.sess.has_errors() {
+                                let msg = format!("Pattern has unexpected type: {} and type {}",
+                                                  def,
+                                                  cmt_pat.ty.repr(tcx));
+                                tcx.sess.span_bug(pat.span, msg[])
+                            }
                         }
 
                         Some(def) => {
                             // Remaining cases are e.g. DefFn, to
                             // which identifiers within patterns
-                            // should not resolve.
-
-                            let msg = format!("Pattern has unexpected def: {}", def);
-                            tcx.sess.span_bug(pat.span, msg[])
+                            // should not resolve. However, we do
+                            // encouter this when using the
+                            // expr-use-visitor during typeck. So just
+                            // ignore it, an error should have been
+                            // reported.
+
+                            if !tcx.sess.has_errors() {
+                                let msg = format!("Pattern has unexpected def: {} and type {}",
+                                                  def,
+                                                  cmt_pat.ty.repr(tcx));
+                                tcx.sess.span_bug(pat.span, msg[])
+                            }
                         }
                     }
                 }
@@ -1197,14 +1226,13 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                     // cases either.
                 }
             }
-        });
+        }));
     }
 
     fn walk_captures(&mut self, closure_expr: &ast::Expr) {
         debug!("walk_captures({})", closure_expr.repr(self.tcx()));
 
-        let tcx = self.typer.tcx();
-        ty::with_freevars(tcx, closure_expr.id, |freevars| {
+        ty::with_freevars(self.tcx(), closure_expr.id, |freevars| {
             match self.tcx().capture_mode(closure_expr.id) {
                 ast::CaptureByRef => {
                     self.walk_by_ref_captures(closure_expr, freevars);
@@ -1221,9 +1249,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                             freevars: &[ty::Freevar]) {
         for freevar in freevars.iter() {
             let id_var = freevar.def.def_id().node;
-            let cmt_var = self.cat_captured_var(closure_expr.id,
-                                                closure_expr.span,
-                                                freevar.def);
+            let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id,
+                                                               closure_expr.span,
+                                                               freevar.def));
 
             // Lookup the kind of borrow the callee requires, as
             // inferred by regionbk
@@ -1244,11 +1272,10 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                               closure_expr: &ast::Expr,
                               freevars: &[ty::Freevar]) {
         for freevar in freevars.iter() {
-            let cmt_var = self.cat_captured_var(closure_expr.id,
-                                                closure_expr.span,
-                                                freevar.def);
-            let mode = copy_or_move(self.tcx(), cmt_var.ty,
-                                    self.param_env, CaptureMove);
+            let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id,
+                                                               closure_expr.span,
+                                                               freevar.def));
+            let mode = copy_or_move(self.typer, &cmt_var, CaptureMove);
             self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode);
         }
     }
@@ -1257,21 +1284,21 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                         closure_id: ast::NodeId,
                         closure_span: Span,
                         upvar_def: def::Def)
-                        -> mc::cmt<'tcx> {
+                        -> mc::McResult<mc::cmt<'tcx>> {
         // Create the cmt for the variable being borrowed, from the
         // caller's perspective
         let var_id = upvar_def.def_id().node;
-        let var_ty = self.typer.node_ty(var_id);
+        let var_ty = try!(self.typer.node_ty(var_id));
         self.mc.cat_def(closure_id, closure_span, var_ty, upvar_def)
     }
 }
 
-fn copy_or_move<'tcx>(tcx: &ty::ctxt<'tcx>,
-                      ty: Ty<'tcx>,
-                      param_env: &ParameterEnvironment<'tcx>,
+fn copy_or_move<'tcx>(typer: &mc::Typer<'tcx>,
+                      cmt: &mc::cmt<'tcx>,
                       move_reason: MoveReason)
-                      -> ConsumeMode {
-    if ty::type_moves_by_default(tcx, ty, param_env) {
+                      -> ConsumeMode
+{
+    if typer.type_moves_by_default(cmt.span, cmt.ty) {
         Move(move_reason)
     } else {
         Copy
diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs
index df06b3b7789..2962b7b7c8e 100644
--- a/src/librustc/middle/intrinsicck.rs
+++ b/src/librustc/middle/intrinsicck.rs
@@ -41,7 +41,7 @@ struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
     // environments for each function we encounter. When we find a
     // call to `transmute`, we can check it in the context of the top
     // of the stack (which ought not to be empty).
-    param_envs: Vec<ty::ParameterEnvironment<'tcx>>,
+    param_envs: Vec<ty::ParameterEnvironment<'a,'tcx>>,
 
     // Dummy sized/unsized types that use to substitute for type
     // parameters in order to estimate how big a type will be for any
@@ -170,6 +170,7 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
 
         let mut substs = param_env.free_substs.clone();
         self.with_each_combination(
+            span,
             param_env,
             param_env.free_substs.types.iter_enumerated(),
             &mut substs,
@@ -187,7 +188,8 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
     }
 
     fn with_each_combination(&self,
-                             param_env: &ty::ParameterEnvironment<'tcx>,
+                             span: Span,
+                             param_env: &ty::ParameterEnvironment<'a,'tcx>,
                              mut types_in_scope: EnumeratedItems<Ty<'tcx>>,
                              substs: &mut Substs<'tcx>,
                              callback: &mut FnMut(&Substs<'tcx>))
@@ -210,15 +212,17 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
                 debug!("with_each_combination: space={}, index={}, param_ty={}",
                        space, index, param_ty.repr(self.tcx));
 
-                if !ty::type_is_sized(self.tcx, param_ty, param_env) {
+                if !ty::type_is_sized(param_env, span, param_ty) {
                     debug!("with_each_combination: param_ty is not known to be sized");
 
                     substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
-                    self.with_each_combination(param_env, types_in_scope.clone(), substs, callback);
+                    self.with_each_combination(span, param_env, types_in_scope.clone(),
+                                               substs, callback);
                 }
 
                 substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
-                self.with_each_combination(param_env, types_in_scope, substs, callback);
+                self.with_each_combination(span, param_env, types_in_scope,
+                                           substs, callback);
             }
         }
     }
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 70942a950e3..722fe82d41c 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -75,7 +75,7 @@ use middle::def;
 use middle::region;
 use middle::ty::{mod, Ty};
 use util::nodemap::{NodeMap};
-use util::ppaux::{ty_to_string, Repr};
+use util::ppaux::{Repr};
 
 use syntax::ast::{MutImmutable, MutMutable};
 use syntax::ast;
@@ -195,51 +195,39 @@ pub enum deref_kind {
 // Categorizes a derefable type.  Note that we include vectors and strings as
 // derefable (we model an index as the combination of a deref and then a
 // pointer adjustment).
-pub fn opt_deref_kind(t: Ty) -> Option<deref_kind> {
+pub fn deref_kind(t: Ty) -> McResult<deref_kind> {
     match t.sty {
         ty::ty_uniq(_) |
         ty::ty_closure(box ty::ClosureTy {store: ty::UniqTraitStore, ..}) => {
-            Some(deref_ptr(Unique))
+            Ok(deref_ptr(Unique))
         }
 
         ty::ty_rptr(r, mt) => {
             let kind = ty::BorrowKind::from_mutbl(mt.mutbl);
-            Some(deref_ptr(BorrowedPtr(kind, *r)))
+            Ok(deref_ptr(BorrowedPtr(kind, *r)))
         }
 
         ty::ty_closure(box ty::ClosureTy {
                 store: ty::RegionTraitStore(r, _),
                 ..
             }) => {
-            Some(deref_ptr(BorrowedPtr(ty::ImmBorrow, r)))
+            Ok(deref_ptr(BorrowedPtr(ty::ImmBorrow, r)))
         }
 
         ty::ty_ptr(ref mt) => {
-            Some(deref_ptr(UnsafePtr(mt.mutbl)))
+            Ok(deref_ptr(UnsafePtr(mt.mutbl)))
         }
 
         ty::ty_enum(..) |
         ty::ty_struct(..) => { // newtype
-            Some(deref_interior(InteriorField(PositionalField(0))))
+            Ok(deref_interior(InteriorField(PositionalField(0))))
         }
 
         ty::ty_vec(_, _) | ty::ty_str => {
-            Some(deref_interior(InteriorElement(element_kind(t))))
+            Ok(deref_interior(InteriorElement(element_kind(t))))
         }
 
-        _ => None
-    }
-}
-
-pub fn deref_kind<'tcx>(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) -> deref_kind {
-    debug!("deref_kind {}", ty_to_string(tcx, t));
-    match opt_deref_kind(t) {
-      Some(k) => k,
-      None => {
-        tcx.sess.bug(
-            format!("deref_kind() invoked on non-derefable type {}",
-                    ty_to_string(tcx, t))[]);
-      }
+        _ => Err(()),
     }
 }
 
@@ -264,6 +252,8 @@ pub struct MemCategorizationContext<'t,TYPER:'t> {
 
 impl<'t,TYPER:'t> Copy for MemCategorizationContext<'t,TYPER> {}
 
+pub type McResult<T> = Result<T, ()>;
+
 /// The `Typer` trait provides the interface for the mem-categorization
 /// module to the results of the type check. It can be used to query
 /// the type assigned to an expression node, to inquire after adjustments,
@@ -282,8 +272,9 @@ impl<'t,TYPER:'t> Copy for MemCategorizationContext<'t,TYPER> {}
 /// can be sure that only `Ok` results will occur.
 pub trait Typer<'tcx> : ty::UnboxedClosureTyper<'tcx> {
     fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
-    fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx>;
-    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx>;
+    fn node_ty(&self, id: ast::NodeId) -> McResult<Ty<'tcx>>;
+    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult<Ty<'tcx>>;
+    fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool;
     fn node_method_ty(&self, method_call: ty::MethodCall) -> Option<Ty<'tcx>>;
     fn node_method_origin(&self, method_call: ty::MethodCall)
                           -> Option<ty::MethodOrigin<'tcx>>;
@@ -382,24 +373,24 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
         self.typer.tcx()
     }
 
-    fn expr_ty(&self, expr: &ast::Expr) -> Ty<'tcx> {
+    fn expr_ty(&self, expr: &ast::Expr) -> McResult<Ty<'tcx>> {
         self.typer.node_ty(expr.id)
     }
 
-    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> {
-        let unadjusted_ty = self.expr_ty(expr);
-        ty::adjust_ty(self.tcx(), expr.span, expr.id, unadjusted_ty,
-                      self.typer.adjustments().borrow().get(&expr.id),
-                      |method_call| self.typer.node_method_ty(method_call))
+    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult<Ty<'tcx>> {
+        let unadjusted_ty = try!(self.expr_ty(expr));
+        Ok(ty::adjust_ty(self.tcx(), expr.span, expr.id, unadjusted_ty,
+                         self.typer.adjustments().borrow().get(&expr.id),
+                         |method_call| self.typer.node_method_ty(method_call)))
     }
 
-    fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> {
+    fn node_ty(&self, id: ast::NodeId) -> McResult<Ty<'tcx>> {
         self.typer.node_ty(id)
     }
 
-    fn pat_ty(&self, pat: &ast::Pat) -> Ty<'tcx> {
+    fn pat_ty(&self, pat: &ast::Pat) -> McResult<Ty<'tcx>> {
         let tcx = self.typer.tcx();
-        let base_ty = self.typer.node_ty(pat.id);
+        let base_ty = try!(self.typer.node_ty(pat.id));
         // FIXME (Issue #18207): This code detects whether we are
         // looking at a `ref x`, and if so, figures out what the type
         // *being borrowed* is.  But ideally we would put in a more
@@ -409,18 +400,19 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                 // a bind-by-ref means that the base_ty will be the type of the ident itself,
                 // but what we want here is the type of the underlying value being borrowed.
                 // So peel off one-level, turning the &T into T.
-                ty::deref(base_ty, false).unwrap_or_else(|| {
-                    panic!("encountered BindByRef with non &-type");
-                }).ty
+                match ty::deref(base_ty, false) {
+                    Some(t) => t.ty,
+                    None => { return Err(()); }
+                }
             }
             _ => base_ty,
         };
         debug!("pat_ty(pat={}) base_ty={} ret_ty={}",
                pat.repr(tcx), base_ty.repr(tcx), ret_ty.repr(tcx));
-        ret_ty
+        Ok(ret_ty)
     }
 
-    pub fn cat_expr(&self, expr: &ast::Expr) -> cmt<'tcx> {
+    pub fn cat_expr(&self, expr: &ast::Expr) -> McResult<cmt<'tcx>> {
         match self.typer.adjustments().borrow().get(&expr.id) {
             None => {
                 // No adjustments.
@@ -434,8 +426,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                                expr.repr(self.tcx()));
                         // Convert a bare fn to a closure by adding NULL env.
                         // Result is an rvalue.
-                        let expr_ty = self.expr_ty_adjusted(expr);
-                        self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)
+                        let expr_ty = try!(self.expr_ty_adjusted(expr));
+                        Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
                     }
 
                     ty::AdjustDerefRef(
@@ -445,8 +437,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                                expr.repr(self.tcx()));
                         // Equivalent to &*expr or something similar.
                         // Result is an rvalue.
-                        let expr_ty = self.expr_ty_adjusted(expr);
-                        self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)
+                        let expr_ty = try!(self.expr_ty_adjusted(expr));
+                        Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
                     }
 
                     ty::AdjustDerefRef(
@@ -463,46 +455,46 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
     pub fn cat_expr_autoderefd(&self,
                                expr: &ast::Expr,
                                autoderefs: uint)
-                               -> cmt<'tcx> {
-        let mut cmt = self.cat_expr_unadjusted(expr);
+                               -> McResult<cmt<'tcx>> {
+        let mut cmt = try!(self.cat_expr_unadjusted(expr));
         debug!("cat_expr_autoderefd: autoderefs={}, cmt={}",
                autoderefs,
                cmt.repr(self.tcx()));
         for deref in range(1u, autoderefs + 1) {
-            cmt = self.cat_deref(expr, cmt, deref, false);
+            cmt = try!(self.cat_deref(expr, cmt, deref, false));
         }
-        return cmt;
+        return Ok(cmt);
     }
 
-    pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> cmt<'tcx> {
+    pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> McResult<cmt<'tcx>> {
         debug!("cat_expr: id={} expr={}", expr.id, expr.repr(self.tcx()));
 
-        let expr_ty = self.expr_ty(expr);
+        let expr_ty = try!(self.expr_ty(expr));
         match expr.node {
           ast::ExprUnary(ast::UnDeref, ref e_base) => {
-            let base_cmt = self.cat_expr(&**e_base);
+            let base_cmt = try!(self.cat_expr(&**e_base));
             self.cat_deref(expr, base_cmt, 0, false)
           }
 
           ast::ExprField(ref base, f_name) => {
-            let base_cmt = self.cat_expr(&**base);
+            let base_cmt = try!(self.cat_expr(&**base));
             debug!("cat_expr(cat_field): id={} expr={} base={}",
                    expr.id,
                    expr.repr(self.tcx()),
                    base_cmt.repr(self.tcx()));
-            self.cat_field(expr, base_cmt, f_name.node.name, expr_ty)
+            Ok(self.cat_field(expr, base_cmt, f_name.node.name, expr_ty))
           }
 
           ast::ExprTupField(ref base, idx) => {
-            let base_cmt = self.cat_expr(&**base);
-            self.cat_tup_field(expr, base_cmt, idx.node, expr_ty)
+            let base_cmt = try!(self.cat_expr(&**base));
+            Ok(self.cat_tup_field(expr, base_cmt, idx.node, expr_ty))
           }
 
           ast::ExprIndex(ref base, ref idx) => {
             match idx.node {
                 ast::ExprRange(..) => {
                     // Slicing syntax special case (KILLME).
-                    self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)
+                    Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
                 }
                 _ => {
                     let method_call = ty::MethodCall::expr(expr.id());
@@ -517,7 +509,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                                                                 ret_ty), 1, true)
                         }
                         None => {
-                            self.cat_index(expr, self.cat_expr(&**base))
+                            self.cat_index(expr, try!(self.cat_expr(&**base)))
                         }
                     }
                 }
@@ -545,7 +537,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
           ast::ExprAgain(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) |
           ast::ExprInlineAsm(..) | ast::ExprBox(..) |
           ast::ExprForLoop(..) => {
-            self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)
+            Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
           }
 
           ast::ExprIfLet(..) => {
@@ -562,43 +554,43 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                    span: Span,
                    expr_ty: Ty<'tcx>,
                    def: def::Def)
-                   -> cmt<'tcx> {
+                   -> McResult<cmt<'tcx>> {
         debug!("cat_def: id={} expr={} def={}",
                id, expr_ty.repr(self.tcx()), def);
 
         match def {
           def::DefStruct(..) | def::DefVariant(..) | def::DefFn(..) |
           def::DefStaticMethod(..) | def::DefConst(..) => {
-                self.cat_rvalue_node(id, span, expr_ty)
+                Ok(self.cat_rvalue_node(id, span, expr_ty))
           }
           def::DefMod(_) | def::DefForeignMod(_) | def::DefUse(_) |
           def::DefTrait(_) | def::DefTy(..) | def::DefPrimTy(_) |
           def::DefTyParam(..) | def::DefTyParamBinder(..) | def::DefRegion(_) |
           def::DefLabel(_) | def::DefSelfTy(..) | def::DefMethod(..) |
           def::DefAssociatedTy(..) | def::DefAssociatedPath(..)=> {
-              Rc::new(cmt_ {
+              Ok(Rc::new(cmt_ {
                   id:id,
                   span:span,
                   cat:cat_static_item,
                   mutbl: McImmutable,
                   ty:expr_ty,
                   note: NoteNone
-              })
+              }))
           }
 
           def::DefStatic(_, mutbl) => {
-              Rc::new(cmt_ {
+              Ok(Rc::new(cmt_ {
                   id:id,
                   span:span,
                   cat:cat_static_item,
                   mutbl: if mutbl { McDeclared } else { McImmutable},
                   ty:expr_ty,
                   note: NoteNone
-              })
+              }))
           }
 
           def::DefUpvar(var_id, fn_node_id, _) => {
-              let ty = self.node_ty(fn_node_id);
+              let ty = try!(self.node_ty(fn_node_id));
               match ty.sty {
                   ty::ty_closure(ref closure_ty) => {
                       // Translate old closure type info into unboxed
@@ -635,14 +627,14 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
           }
 
           def::DefLocal(vid) => {
-            Rc::new(cmt_ {
+            Ok(Rc::new(cmt_ {
                 id: id,
                 span: span,
                 cat: cat_local(vid),
                 mutbl: MutabilityCategory::from_local(self.tcx(), vid),
                 ty: expr_ty,
                 note: NoteNone
-            })
+            }))
           }
         }
     }
@@ -657,7 +649,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                  kind: ty::UnboxedClosureKind,
                  mode: ast::CaptureClause,
                  is_unboxed: bool)
-                 -> cmt<'tcx> {
+                 -> McResult<cmt<'tcx>> {
         // An upvar can have up to 3 components.  The base is a
         // `cat_upvar`.  Next, we add a deref through the implicit
         // environment pointer with an anonymous free region 'env and
@@ -679,7 +671,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
         // FnOnce         | copied               | upvar -> &'up bk
         // old stack      | N/A                  | upvar -> &'env mut -> &'up bk
         // old proc/once  | copied               | N/A
-        let var_ty = self.node_ty(var_id);
+        let var_ty = try!(self.node_ty(var_id));
 
         let upvar_id = ty::UpvarId { var_id: var_id,
                                      closure_expr_id: fn_node_id };
@@ -727,7 +719,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
         });
 
         // First, switch by capture mode
-        match mode {
+        Ok(match mode {
             ast::CaptureByValue => {
                 let mut base = cmt_ {
                     id: id,
@@ -809,7 +801,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                     note: NoteUpvarRef(upvar_id)
                 })
             }
-        }
+        })
     }
 
     pub fn cat_rvalue_node(&self,
@@ -882,7 +874,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                              base_cmt: cmt<'tcx>,
                              deref_cnt: uint,
                              implicit: bool)
-                             -> cmt<'tcx> {
+                             -> McResult<cmt<'tcx>> {
         let adjustment = match self.typer.adjustments().borrow().get(&node.id()) {
             Some(adj) if ty::adjust_is_object(adj) => ty::AutoObject,
             _ if deref_cnt != 0 => ty::AutoDeref(deref_cnt),
@@ -896,7 +888,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
         let method_ty = self.typer.node_method_ty(method_call);
 
         debug!("cat_deref: method_call={} method_ty={}",
-            method_call, method_ty.map(|ty| ty.repr(self.tcx())));
+               method_call, method_ty.map(|ty| ty.repr(self.tcx())));
 
         let base_cmt = match method_ty {
             Some(method_ty) => {
@@ -905,13 +897,13 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
             }
             None => base_cmt
         };
-        match ty::deref(base_cmt.ty, true) {
+        let base_cmt_ty = base_cmt.ty;
+        match ty::deref(base_cmt_ty, true) {
             Some(mt) => self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty, implicit),
             None => {
-                self.tcx().sess.span_bug(
-                    node.span(),
-                    format!("Explicit deref of non-derefable type: {}",
-                            base_cmt.ty.repr(self.tcx()))[]);
+                debug!("Explicit deref of non-derefable type: {}",
+                       base_cmt_ty.repr(self.tcx()));
+                return Err(());
             }
         }
     }
@@ -922,8 +914,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                                     deref_cnt: uint,
                                     deref_ty: Ty<'tcx>,
                                     implicit: bool)
-                                    -> cmt<'tcx> {
-        let (m, cat) = match deref_kind(self.tcx(), base_cmt.ty) {
+                                    -> McResult<cmt<'tcx>>
+    {
+        let (m, cat) = match try!(deref_kind(base_cmt.ty)) {
             deref_ptr(ptr) => {
                 let ptr = if implicit {
                     match ptr {
@@ -943,20 +936,20 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                 (base_cmt.mutbl.inherit(), cat_interior(base_cmt, interior))
             }
         };
-        Rc::new(cmt_ {
+        Ok(Rc::new(cmt_ {
             id: node.id(),
             span: node.span(),
             cat: cat,
             mutbl: m,
             ty: deref_ty,
             note: NoteNone
-        })
+        }))
     }
 
     pub fn cat_index<N:ast_node>(&self,
                                  elt: &N,
                                  mut base_cmt: cmt<'tcx>)
-                                 -> cmt<'tcx> {
+                                 -> McResult<cmt<'tcx>> {
         //! Creates a cmt for an indexing operation (`[]`).
         //!
         //! One subtle aspect of indexing that may not be
@@ -987,17 +980,14 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                 match ty::array_element_ty(self.tcx(), base_cmt.ty) {
                     Some(ty) => ty,
                     None => {
-                        self.tcx().sess.span_bug(
-                            elt.span(),
-                            format!("Explicit index of non-index type `{}`",
-                                    base_cmt.ty.repr(self.tcx()))[]);
+                        return Err(());
                     }
                 }
             }
         };
 
         let m = base_cmt.mutbl.inherit();
-        return interior(elt, base_cmt.clone(), base_cmt.ty, m, element_ty);
+        return Ok(interior(elt, base_cmt.clone(), base_cmt.ty, m, element_ty));
 
         fn interior<'tcx, N: ast_node>(elt: &N,
                                        of_cmt: cmt<'tcx>,
@@ -1021,15 +1011,16 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
     fn deref_vec<N:ast_node>(&self,
                              elt: &N,
                              base_cmt: cmt<'tcx>)
-                             -> cmt<'tcx> {
-        match deref_kind(self.tcx(), base_cmt.ty) {
+                             -> McResult<cmt<'tcx>>
+    {
+        match try!(deref_kind(base_cmt.ty)) {
             deref_ptr(ptr) => {
                 // for unique ptrs, we inherit mutability from the
                 // owning reference.
                 let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr);
 
                 // the deref is explicit in the resulting cmt
-                Rc::new(cmt_ {
+                Ok(Rc::new(cmt_ {
                     id:elt.id(),
                     span:elt.span(),
                     cat:cat_deref(base_cmt.clone(), 0, ptr),
@@ -1039,11 +1030,11 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                         None => self.tcx().sess.bug("Found non-derefable type")
                     },
                     note: NoteNone
-                })
+                }))
             }
 
             deref_interior(_) => {
-                base_cmt
+                Ok(base_cmt)
             }
         }
     }
@@ -1058,13 +1049,13 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
     pub fn cat_slice_pattern(&self,
                              vec_cmt: cmt<'tcx>,
                              slice_pat: &ast::Pat)
-                             -> (cmt<'tcx>, ast::Mutability, ty::Region) {
-        let slice_ty = self.node_ty(slice_pat.id);
+                             -> McResult<(cmt<'tcx>, ast::Mutability, ty::Region)> {
+        let slice_ty = try!(self.node_ty(slice_pat.id));
         let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(),
                                                     slice_pat,
                                                     slice_ty);
-        let cmt_slice = self.cat_index(slice_pat, self.deref_vec(slice_pat, vec_cmt));
-        return (cmt_slice, slice_mutbl, slice_r);
+        let cmt_slice = try!(self.cat_index(slice_pat, try!(self.deref_vec(slice_pat, vec_cmt))));
+        return Ok((cmt_slice, slice_mutbl, slice_r));
 
         /// In a pattern like [a, b, ..c], normally `c` has slice type, but if you have [a, b,
         /// ..ref c], then the type of `ref c` will be `&&[]`, so to extract the slice details we
@@ -1119,15 +1110,16 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
         })
     }
 
-    pub fn cat_pattern<F>(&self, cmt: cmt<'tcx>, pat: &ast::Pat, mut op: F) where
-        F: FnMut(&MemCategorizationContext<'t, TYPER>, cmt<'tcx>, &ast::Pat),
+    pub fn cat_pattern<F>(&self, cmt: cmt<'tcx>, pat: &ast::Pat, mut op: F) -> McResult<()>
+        where F: FnMut(&MemCategorizationContext<'t, TYPER>, cmt<'tcx>, &ast::Pat),
     {
         self.cat_pattern_(cmt, pat, &mut op)
     }
 
     // FIXME(#19596) This is a workaround, but there should be a better way to do this
-    fn cat_pattern_<F>(&self, cmt: cmt<'tcx>, pat: &ast::Pat, op: &mut F) where
-        F: FnMut(&MemCategorizationContext<'t, TYPER>, cmt<'tcx>, &ast::Pat),
+    fn cat_pattern_<F>(&self, cmt: cmt<'tcx>, pat: &ast::Pat, op: &mut F)
+                       -> McResult<()>
+        where F : FnMut(&MemCategorizationContext<'t, TYPER>, cmt<'tcx>, &ast::Pat),
     {
         // Here, `cmt` is the categorization for the value being
         // matched and pat is the pattern it is being matched against.
@@ -1208,29 +1200,29 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
                 Some(&def::DefVariant(..)) => {
                     // variant(x, y, z)
                     for (i, subpat) in subpats.iter().enumerate() {
-                        let subpat_ty = self.pat_ty(&**subpat); // see (*2)
+                        let subpat_ty = try!(self.pat_ty(&**subpat)); // see (*2)
 
                         let subcmt =
                             self.cat_imm_interior(
                                 pat, cmt.clone(), subpat_ty,
                                 InteriorField(PositionalField(i)));
 
-                        self.cat_pattern_(subcmt, &**subpat, op);
+                        try!(self.cat_pattern_(subcmt, &**subpat, op));
                     }
                 }
                 Some(&def::DefStruct(..)) => {
                     for (i, subpat) in subpats.iter().enumerate() {
-                        let subpat_ty = self.pat_ty(&**subpat); // see (*2)
+                        let subpat_ty = try!(self.pat_ty(&**subpat)); // see (*2)
                         let cmt_field =
                             self.cat_imm_interior(
                                 pat, cmt.clone(), subpat_ty,
                                 InteriorField(PositionalField(i)));
-                        self.cat_pattern_(cmt_field, &**subpat, op);
+                        try!(self.cat_pattern_(cmt_field, &**subpat, op));
                     }
                 }
                 Some(&def::DefConst(..)) => {
                     for subpat in subpats.iter() {
-                        self.cat_pattern_(cmt.clone(), &**subpat, op);
+                        try!(self.cat_pattern_(cmt.clone(), &**subpat, op));
                     }
                 }
                 _ => {
@@ -1242,7 +1234,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
           }
 
           ast::PatIdent(_, _, Some(ref subpat)) => {
-              self.cat_pattern_(cmt, &**subpat, op);
+              try!(self.cat_pattern_(cmt, &**subpat, op));
           }
 
           ast::PatIdent(_, _, None) => {
@@ -1252,42 +1244,42 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
           ast::PatStruct(_, ref field_pats, _) => {
             // {f1: p1, ..., fN: pN}
             for fp in field_pats.iter() {
-                let field_ty = self.pat_ty(&*fp.node.pat); // see (*2)
+                let field_ty = try!(self.pat_ty(&*fp.node.pat)); // see (*2)
                 let cmt_field = self.cat_field(pat, cmt.clone(), fp.node.ident.name, field_ty);
-                self.cat_pattern_(cmt_field, &*fp.node.pat, op);
+                try!(self.cat_pattern_(cmt_field, &*fp.node.pat, op));
             }
           }
 
           ast::PatTup(ref subpats) => {
             // (p1, ..., pN)
             for (i, subpat) in subpats.iter().enumerate() {
-                let subpat_ty = self.pat_ty(&**subpat); // see (*2)
+                let subpat_ty = try!(self.pat_ty(&**subpat)); // see (*2)
                 let subcmt =
                     self.cat_imm_interior(
                         pat, cmt.clone(), subpat_ty,
                         InteriorField(PositionalField(i)));
-                self.cat_pattern_(subcmt, &**subpat, op);
+                try!(self.cat_pattern_(subcmt, &**subpat, op));
             }
           }
 
           ast::PatBox(ref subpat) | ast::PatRegion(ref subpat) => {
             // @p1, ~p1, ref p1
-            let subcmt = self.cat_deref(pat, cmt, 0, false);
-            self.cat_pattern_(subcmt, &**subpat, op);
+            let subcmt = try!(self.cat_deref(pat, cmt, 0, false));
+              try!(self.cat_pattern_(subcmt, &**subpat, op));
           }
 
           ast::PatVec(ref before, ref slice, ref after) => {
-              let elt_cmt = self.cat_index(pat, self.deref_vec(pat, cmt));
+              let elt_cmt = try!(self.cat_index(pat, try!(self.deref_vec(pat, cmt))));
               for before_pat in before.iter() {
-                  self.cat_pattern_(elt_cmt.clone(), &**before_pat, op);
+                  try!(self.cat_pattern_(elt_cmt.clone(), &**before_pat, op));
               }
               for slice_pat in slice.iter() {
-                  let slice_ty = self.pat_ty(&**slice_pat);
+                  let slice_ty = try!(self.pat_ty(&**slice_pat));
                   let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty);
-                  self.cat_pattern_(slice_cmt, &**slice_pat, op);
+                  try!(self.cat_pattern_(slice_cmt, &**slice_pat, op));
               }
               for after_pat in after.iter() {
-                  self.cat_pattern_(elt_cmt.clone(), &**after_pat, op);
+                  try!(self.cat_pattern_(elt_cmt.clone(), &**after_pat, op));
               }
           }
 
@@ -1299,77 +1291,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
               self.tcx().sess.span_bug(pat.span, "unexpanded macro");
           }
         }
-    }
 
-    pub fn cmt_to_string(&self, cmt: &cmt_<'tcx>) -> String {
-        fn upvar_to_string(upvar: &Upvar, is_copy: bool) -> String {
-            if upvar.is_unboxed {
-                let kind = match upvar.kind {
-                    ty::FnUnboxedClosureKind => "Fn",
-                    ty::FnMutUnboxedClosureKind => "FnMut",
-                    ty::FnOnceUnboxedClosureKind => "FnOnce"
-                };
-                format!("captured outer variable in an `{}` closure", kind)
-            } else {
-                (match (upvar.kind, is_copy) {
-                    (ty::FnOnceUnboxedClosureKind, true) => "captured outer variable in a proc",
-                    _ => "captured outer variable"
-                }).to_string()
-            }
-        }
-
-        match cmt.cat {
-          cat_static_item => {
-              "static item".to_string()
-          }
-          cat_rvalue(..) => {
-              "non-lvalue".to_string()
-          }
-          cat_local(vid) => {
-              match self.tcx().map.find(vid) {
-                  Some(ast_map::NodeArg(_)) => {
-                      "argument".to_string()
-                  }
-                  _ => "local variable".to_string()
-              }
-          }
-          cat_deref(_, _, pk) => {
-              let upvar = cmt.upvar();
-              match upvar.as_ref().map(|i| &i.cat) {
-                  Some(&cat_upvar(ref var)) => {
-                      upvar_to_string(var, false)
-                  }
-                  Some(_) => unreachable!(),
-                  None => {
-                      match pk {
-                          Implicit(..) => {
-                            "dereference (dereference is implicit, due to indexing)".to_string()
-                          }
-                          Unique => format!("dereference of `{}`", ptr_sigil(pk)),
-                          _ => format!("dereference of `{}`-pointer", ptr_sigil(pk))
-                      }
-                  }
-              }
-          }
-          cat_interior(_, InteriorField(NamedField(_))) => {
-              "field".to_string()
-          }
-          cat_interior(_, InteriorField(PositionalField(_))) => {
-              "anonymous field".to_string()
-          }
-          cat_interior(_, InteriorElement(VecElement)) => {
-              "vec content".to_string()
-          }
-          cat_interior(_, InteriorElement(OtherElement)) => {
-              "indexed content".to_string()
-          }
-          cat_upvar(ref var) => {
-              upvar_to_string(var, true)
-          }
-          cat_downcast(ref cmt, _) => {
-            self.cmt_to_string(&**cmt)
-          }
-        }
+        Ok(())
     }
 }
 
@@ -1481,6 +1404,78 @@ impl<'tcx> cmt_<'tcx> {
             NoteNone => None
         }
     }
+
+
+    pub fn descriptive_string(&self, tcx: &ty::ctxt) -> String {
+        fn upvar_to_string(upvar: &Upvar, is_copy: bool) -> String {
+            if upvar.is_unboxed {
+                let kind = match upvar.kind {
+                    ty::FnUnboxedClosureKind => "Fn",
+                    ty::FnMutUnboxedClosureKind => "FnMut",
+                    ty::FnOnceUnboxedClosureKind => "FnOnce"
+                };
+                format!("captured outer variable in an `{}` closure", kind)
+            } else {
+                (match (upvar.kind, is_copy) {
+                    (ty::FnOnceUnboxedClosureKind, true) => "captured outer variable in a proc",
+                    _ => "captured outer variable"
+                }).to_string()
+            }
+        }
+
+        match self.cat {
+            cat_static_item => {
+                "static item".to_string()
+            }
+            cat_rvalue(..) => {
+                "non-lvalue".to_string()
+            }
+            cat_local(vid) => {
+                match tcx.map.find(vid) {
+                    Some(ast_map::NodeArg(_)) => {
+                        "argument".to_string()
+                    }
+                    _ => "local variable".to_string()
+                }
+            }
+            cat_deref(_, _, pk) => {
+                let upvar = self.upvar();
+                match upvar.as_ref().map(|i| &i.cat) {
+                    Some(&cat_upvar(ref var)) => {
+                        upvar_to_string(var, false)
+                    }
+                    Some(_) => unreachable!(),
+                    None => {
+                        match pk {
+                            Implicit(..) => {
+                                "dereference (dereference is implicit, due to indexing)".to_string()
+                            }
+                            Unique => format!("dereference of `{}`", ptr_sigil(pk)),
+                            _ => format!("dereference of `{}`-pointer", ptr_sigil(pk))
+                        }
+                    }
+                }
+            }
+            cat_interior(_, InteriorField(NamedField(_))) => {
+                "field".to_string()
+            }
+            cat_interior(_, InteriorField(PositionalField(_))) => {
+                "anonymous field".to_string()
+            }
+            cat_interior(_, InteriorElement(VecElement)) => {
+                "vec content".to_string()
+            }
+            cat_interior(_, InteriorElement(OtherElement)) => {
+                "indexed content".to_string()
+            }
+            cat_upvar(ref var) => {
+                upvar_to_string(var, true)
+            }
+            cat_downcast(ref cmt, _) => {
+                cmt.descriptive_string(tcx)
+            }
+        }
+    }
 }
 
 impl<'tcx> Repr<'tcx> for cmt_<'tcx> {
diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs
index 4aff36c2624..6a8b6d49cc0 100644
--- a/src/librustc/middle/traits/coherence.rs
+++ b/src/librustc/middle/traits/coherence.rs
@@ -42,8 +42,8 @@ pub fn impl_can_satisfy(infcx: &InferCtxt,
 
     // Determine whether `impl2` can provide an implementation for those
     // same types.
-    let param_env = ty::empty_parameter_environment();
-    let mut selcx = SelectionContext::intercrate(infcx, &param_env, infcx.tcx);
+    let param_env = ty::empty_parameter_environment(infcx.tcx);
+    let mut selcx = SelectionContext::intercrate(infcx, &param_env);
     let obligation = Obligation::new(ObligationCause::dummy(),
                                      ty::Binder(ty::TraitPredicate {
                                          trait_ref: Rc::new(impl1_trait_ref),
diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs
index a0413701abc..05ea2f9a7d2 100644
--- a/src/librustc/middle/traits/error_reporting.rs
+++ b/src/librustc/middle/traits/error_reporting.rs
@@ -77,7 +77,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                     "overflow evaluating the requirement `{}`",
                     predicate.user_string(infcx.tcx)).as_slice());
 
-            suggest_new_overflow_limit(infcx, obligation.cause.span);
+            suggest_new_overflow_limit(infcx.tcx, obligation.cause.span);
 
             note_obligation_cause(infcx, obligation);
         }
@@ -332,10 +332,10 @@ fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
     }
 }
 
-pub fn suggest_new_overflow_limit(infcx: &InferCtxt, span: Span) {
-    let current_limit = infcx.tcx.sess.recursion_limit.get();
+pub fn suggest_new_overflow_limit(tcx: &ty::ctxt, span: Span) {
+    let current_limit = tcx.sess.recursion_limit.get();
     let suggested_limit = current_limit * 2;
-    infcx.tcx.sess.span_note(
+    tcx.sess.span_note(
         span,
         format!(
             "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs
index 2dbb15b215e..e8a22e3d1d8 100644
--- a/src/librustc/middle/traits/fulfill.rs
+++ b/src/librustc/middle/traits/fulfill.rs
@@ -109,7 +109,6 @@ impl<'tcx> FulfillmentContext<'tcx> {
     /// `projection_ty` again.
     pub fn normalize_projection_type<'a>(&mut self,
                                          infcx: &InferCtxt<'a,'tcx>,
-                                         param_env: &ty::ParameterEnvironment<'tcx>,
                                          typer: &ty::UnboxedClosureTyper<'tcx>,
                                          projection_ty: ty::ProjectionTy<'tcx>,
                                          cause: ObligationCause<'tcx>)
@@ -122,7 +121,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
 
         // FIXME(#20304) -- cache
 
-        let mut selcx = SelectionContext::new(infcx, param_env, typer);
+        let mut selcx = SelectionContext::new(infcx, typer);
         let normalized = project::normalize_projection_type(&mut selcx, projection_ty, cause, 0);
 
         for obligation in normalized.obligations.into_iter() {
@@ -186,11 +185,10 @@ impl<'tcx> FulfillmentContext<'tcx> {
 
     pub fn select_all_or_error<'a>(&mut self,
                                    infcx: &InferCtxt<'a,'tcx>,
-                                   param_env: &ty::ParameterEnvironment<'tcx>,
                                    typer: &ty::UnboxedClosureTyper<'tcx>)
                                    -> Result<(),Vec<FulfillmentError<'tcx>>>
     {
-        try!(self.select_where_possible(infcx, param_env, typer));
+        try!(self.select_where_possible(infcx, typer));
 
         // Anything left is ambiguous.
         let errors: Vec<FulfillmentError> =
@@ -212,21 +210,19 @@ impl<'tcx> FulfillmentContext<'tcx> {
     /// results in `O(n^2)` performance (#18208).
     pub fn select_new_obligations<'a>(&mut self,
                                       infcx: &InferCtxt<'a,'tcx>,
-                                      param_env: &ty::ParameterEnvironment<'tcx>,
                                       typer: &ty::UnboxedClosureTyper<'tcx>)
                                       -> Result<(),Vec<FulfillmentError<'tcx>>>
     {
-        let mut selcx = SelectionContext::new(infcx, param_env, typer);
+        let mut selcx = SelectionContext::new(infcx, typer);
         self.select(&mut selcx, true)
     }
 
     pub fn select_where_possible<'a>(&mut self,
                                      infcx: &InferCtxt<'a,'tcx>,
-                                     param_env: &ty::ParameterEnvironment<'tcx>,
                                      typer: &ty::UnboxedClosureTyper<'tcx>)
                                      -> Result<(),Vec<FulfillmentError<'tcx>>>
     {
-        let mut selcx = SelectionContext::new(infcx, param_env, typer);
+        let mut selcx = SelectionContext::new(infcx, typer);
         self.select(&mut selcx, false)
     }
 
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index c83898bcd8a..1a1d52a047c 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -15,6 +15,7 @@ pub use self::FulfillmentErrorCode::*;
 pub use self::Vtable::*;
 pub use self::ObligationCauseCode::*;
 
+use middle::mem_categorization::Typer;
 use middle::subst;
 use middle::ty::{mod, Ty};
 use middle::infer::InferCtxt;
@@ -22,9 +23,10 @@ use std::slice::Iter;
 use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap::{Span, DUMMY_SP};
-use util::ppaux::Repr;
+use util::ppaux::{Repr, UserString};
 
 pub use self::error_reporting::report_fulfillment_errors;
+pub use self::error_reporting::suggest_new_overflow_limit;
 pub use self::coherence::orphan_check;
 pub use self::coherence::OrphanCheckErr;
 pub use self::fulfill::{FulfillmentContext, RegionObligation};
@@ -288,11 +290,12 @@ pub fn predicates_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>,
 /// `bound` or is not known to meet bound (note that this is
 /// conservative towards *no impl*, which is the opposite of the
 /// `evaluate` methods).
-pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
-                                                 param_env: &ty::ParameterEnvironment<'tcx>,
-                                                 ty: Ty<'tcx>,
-                                                 bound: ty::BuiltinBound)
-                                                 -> bool
+pub fn evaluate_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
+                                       typer: &ty::UnboxedClosureTyper<'tcx>,
+                                       ty: Ty<'tcx>,
+                                       bound: ty::BuiltinBound,
+                                       span: Span)
+                                       -> SelectionResult<'tcx, ()>
 {
     debug!("type_known_to_meet_builtin_bound(ty={}, bound={})",
            ty.repr(infcx.tcx),
@@ -300,17 +303,49 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
 
     let mut fulfill_cx = FulfillmentContext::new();
 
-    // We can use dummy values here because we won't report any errors
-    // that result nor will we pay any mind to region obligations that arise
-    // (there shouldn't really be any anyhow).
-    let cause = ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID);
+    // We can use a dummy node-id here because we won't pay any mind
+    // to region obligations that arise (there shouldn't really be any
+    // anyhow).
+    let cause = ObligationCause::misc(span, ast::DUMMY_NODE_ID);
 
     fulfill_cx.register_builtin_bound(infcx, ty, bound, cause);
 
     // Note: we only assume something is `Copy` if we can
     // *definitively* show that it implements `Copy`. Otherwise,
     // assume it is move; linear is always ok.
-    let result = fulfill_cx.select_all_or_error(infcx, param_env, infcx.tcx).is_ok();
+    let result = match fulfill_cx.select_all_or_error(infcx, typer) {
+        Ok(()) => Ok(Some(())), // Success, we know it implements Copy.
+        Err(errors) => {
+            // Check if overflow occurred anywhere and propagate that.
+            if errors.iter().any(
+                |err| match err.code { CodeSelectionError(Overflow) => true, _ => false })
+            {
+                return Err(Overflow);
+            }
+
+            // Otherwise, if there were any hard errors, propagate an
+            // arbitrary one of those. If no hard errors at all,
+            // report ambiguity.
+            let sel_error =
+                errors.iter()
+                      .filter_map(|err| {
+                          match err.code {
+                              CodeAmbiguity => None,
+                              CodeSelectionError(ref e) => Some(e.clone()),
+                              CodeProjectionError(_) => {
+                                  infcx.tcx.sess.span_bug(
+                                      span,
+                                      "projection error while selecting?")
+                              }
+                          }
+                      })
+                      .next();
+            match sel_error {
+                None => { Ok(None) }
+                Some(e) => { Err(e) }
+            }
+        }
+    };
 
     debug!("type_known_to_meet_builtin_bound: ty={} bound={} result={}",
            ty.repr(infcx.tcx),
@@ -320,6 +355,40 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
     result
 }
 
+pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
+                                                 typer: &ty::UnboxedClosureTyper<'tcx>,
+                                                 ty: Ty<'tcx>,
+                                                 bound: ty::BuiltinBound,
+                                                 span: Span)
+                                                 -> bool
+{
+    match evaluate_builtin_bound(infcx, typer, ty, bound, span) {
+        Ok(Some(())) => {
+            // definitely impl'd
+            true
+        }
+        Ok(None) => {
+            // ambiguous: if coherence check was successful, shouldn't
+            // happen, but we might have reported an error and been
+            // soldering on, so just treat this like not implemented
+            false
+        }
+        Err(Overflow) => {
+            infcx.tcx.sess.span_err(
+                span,
+                format!("overflow evaluating whether `{}` is `{}`",
+                        ty.user_string(infcx.tcx),
+                        bound.user_string(infcx.tcx))[]);
+            suggest_new_overflow_limit(infcx.tcx, span);
+            false
+        }
+        Err(_) => {
+            // other errors: not implemented.
+            false
+        }
+    }
+}
+
 impl<'tcx,O> Obligation<'tcx,O> {
     pub fn new(cause: ObligationCause<'tcx>,
                trait_ref: O)
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index d09f2a250b0..7da33babaeb 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -46,7 +46,6 @@ use util::ppaux::Repr;
 
 pub struct SelectionContext<'cx, 'tcx:'cx> {
     infcx: &'cx InferCtxt<'cx, 'tcx>,
-    param_env: &'cx ty::ParameterEnvironment<'tcx>,
     closure_typer: &'cx (ty::UnboxedClosureTyper<'tcx>+'cx),
 
     /// Freshener used specifically for skolemizing entries on the
@@ -181,12 +180,10 @@ enum EvaluationResult<'tcx> {
 
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>,
-               param_env: &'cx ty::ParameterEnvironment<'tcx>,
                closure_typer: &'cx ty::UnboxedClosureTyper<'tcx>)
                -> SelectionContext<'cx, 'tcx> {
         SelectionContext {
             infcx: infcx,
-            param_env: param_env,
             closure_typer: closure_typer,
             freshener: infcx.freshener(),
             intercrate: false,
@@ -194,12 +191,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     }
 
     pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>,
-                      param_env: &'cx ty::ParameterEnvironment<'tcx>,
                       closure_typer: &'cx ty::UnboxedClosureTyper<'tcx>)
                       -> SelectionContext<'cx, 'tcx> {
         SelectionContext {
             infcx: infcx,
-            param_env: param_env,
             closure_typer: closure_typer,
             freshener: infcx.freshener(),
             intercrate: true,
@@ -210,14 +205,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         self.infcx
     }
 
-    pub fn param_env(&self) -> &'cx ty::ParameterEnvironment<'tcx> {
-        self.param_env
-    }
-
     pub fn tcx(&self) -> &'cx ty::ctxt<'tcx> {
         self.infcx.tcx
     }
 
+    pub fn param_env(&self) -> &'cx ty::ParameterEnvironment<'cx, 'tcx> {
+        self.closure_typer.param_env()
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Selection
     //
@@ -650,7 +645,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // it's not worth going to more trouble to increase the
         // hit-rate I don't think.
         if self.intercrate {
-            return &self.param_env.selection_cache;
+            return &self.param_env().selection_cache;
         }
 
         // If the trait refers to any parameters in scope, then use
@@ -659,7 +654,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             cache_fresh_trait_pred.0.input_types().iter().any(
                 |&t| ty::type_has_self(t) || ty::type_has_params(t))
         {
-            return &self.param_env.selection_cache;
+            return &self.param_env().selection_cache;
         }
 
         // If the trait refers to unbound type variables, and there
@@ -668,11 +663,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // common case, then we can use the global environment.
         // See the discussion in doc.rs for more details.
         if
-            !self.param_env.caller_bounds.is_empty() &&
+            !self.param_env().caller_bounds.is_empty() &&
             cache_fresh_trait_pred.0.input_types().iter().any(
                 |&t| ty::type_has_ty_infer(t))
         {
-            return &self.param_env.selection_cache;
+            return &self.param_env().selection_cache;
         }
 
         // Otherwise, we can use the global cache.
@@ -902,7 +897,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                obligation.repr(self.tcx()));
 
         let caller_trait_refs: Vec<_> =
-            self.param_env.caller_bounds.predicates.iter()
+            self.param_env().caller_bounds.predicates.iter()
             .filter_map(|o| o.to_opt_poly_trait_ref())
             .collect();
 
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 999bc23c270..c720032bef2 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -2058,7 +2058,9 @@ impl<'tcx> TraitRef<'tcx> {
 /// future I hope to refine the representation of types so as to make
 /// more distinctions clearer.
 #[deriving(Clone)]
-pub struct ParameterEnvironment<'tcx> {
+pub struct ParameterEnvironment<'a, 'tcx:'a> {
+    pub tcx: &'a ctxt<'tcx>,
+
     /// A substitution that can be applied to move from
     /// the "outer" view of a type or method to the "inner" view.
     /// In general, this means converting from bound parameters to
@@ -2082,8 +2084,8 @@ pub struct ParameterEnvironment<'tcx> {
     pub selection_cache: traits::SelectionCache<'tcx>,
 }
 
-impl<'tcx> ParameterEnvironment<'tcx> {
-    pub fn for_item(cx: &ctxt<'tcx>, id: NodeId) -> ParameterEnvironment<'tcx> {
+impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
+    pub fn for_item(cx: &'a ctxt<'tcx>, id: NodeId) -> ParameterEnvironment<'a, 'tcx> {
         match cx.map.find(id) {
             Some(ast_map::NodeImplItem(ref impl_item)) => {
                 match **impl_item {
@@ -2272,6 +2274,8 @@ impl UnboxedClosureKind {
 }
 
 pub trait UnboxedClosureTyper<'tcx> {
+    fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx>;
+
     fn unboxed_closure_kind(&self,
                             def_id: ast::DefId)
                             -> ty::UnboxedClosureKind;
@@ -2424,6 +2428,21 @@ impl<'tcx> ctxt<'tcx> {
         self.region_interner.borrow_mut().insert(region, region);
         region
     }
+
+    pub fn unboxed_closure_kind(&self,
+                            def_id: ast::DefId)
+                            -> ty::UnboxedClosureKind
+    {
+        self.unboxed_closures.borrow()[def_id].kind
+    }
+
+    pub fn unboxed_closure_type(&self,
+                            def_id: ast::DefId,
+                            substs: &subst::Substs<'tcx>)
+                            -> ty::ClosureTy<'tcx>
+    {
+        self.unboxed_closures.borrow()[def_id].closure_type.subst(self, substs)
+    }
 }
 
 // Interns a type/name combination, stores the resulting box in cx.interner,
@@ -3377,7 +3396,8 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents {
             ty_unboxed_closure(did, r, substs) => {
                 // FIXME(#14449): `borrowed_contents` below assumes `&mut`
                 // unboxed closure.
-                let upvars = unboxed_closure_upvars(cx, did, substs).unwrap();
+                let param_env = ty::empty_parameter_environment(cx);
+                let upvars = unboxed_closure_upvars(&param_env, did, substs).unwrap();
                 TypeContents::union(upvars.as_slice(),
                                     |f| tc_ty(cx, f.ty, cache))
                     | borrowed_contents(*r, MutMutable)
@@ -3526,12 +3546,12 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents {
     }
 }
 
-fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>,
-                          cache: &RefCell<HashMap<Ty<'tcx>,bool>>,
-                          param_env: &ParameterEnvironment<'tcx>,
-                          ty: Ty<'tcx>,
-                          bound: ty::BuiltinBound)
-                          -> bool
+fn type_impls_bound<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>,
+                             cache: &RefCell<HashMap<Ty<'tcx>,bool>>,
+                             ty: Ty<'tcx>,
+                             bound: ty::BuiltinBound,
+                             span: Span)
+                             -> bool
 {
     assert!(!ty::type_needs_infer(ty));
 
@@ -3540,7 +3560,7 @@ fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>,
             None => {}
             Some(&result) => {
                 debug!("type_impls_bound({}, {}) = {} (cached)",
-                       ty_to_string(cx, ty),
+                       ty.repr(param_env.tcx),
                        bound,
                        result);
                 return result
@@ -3548,11 +3568,12 @@ fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>,
         }
     }
 
-    let infcx = infer::new_infer_ctxt(cx);
-    let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound);
+    let infcx = infer::new_infer_ctxt(param_env.tcx);
+
+    let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound, span);
 
     debug!("type_impls_bound({}, {}) = {}",
-           ty_to_string(cx, ty),
+           ty.repr(param_env.tcx),
            bound,
            is_impld);
 
@@ -3564,20 +3585,22 @@ fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>,
     is_impld
 }
 
-pub fn type_moves_by_default<'tcx>(cx: &ctxt<'tcx>,
-                                   ty: Ty<'tcx>,
-                                   param_env: &ParameterEnvironment<'tcx>)
-                                   -> bool
+pub fn type_moves_by_default<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>,
+                                      span: Span,
+                                      ty: Ty<'tcx>)
+                                      -> bool
 {
-    !type_impls_bound(cx, &cx.type_impls_copy_cache, param_env, ty, ty::BoundCopy)
+    let tcx = param_env.tcx;
+    !type_impls_bound(param_env, &tcx.type_impls_copy_cache, ty, ty::BoundCopy, span)
 }
 
-pub fn type_is_sized<'tcx>(cx: &ctxt<'tcx>,
-                           ty: Ty<'tcx>,
-                           param_env: &ParameterEnvironment<'tcx>)
-                           -> bool
+pub fn type_is_sized<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>,
+                              span: Span,
+                              ty: Ty<'tcx>)
+                              -> bool
 {
-    type_impls_bound(cx, &cx.type_impls_sized_cache, param_env, ty, ty::BoundSized)
+    let tcx = param_env.tcx;
+    type_impls_bound(param_env, &tcx.type_impls_sized_cache, ty, ty::BoundSized, span)
 }
 
 pub fn is_ffi_safe<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
@@ -3622,8 +3645,6 @@ pub fn is_instantiable<'tcx>(cx: &ctxt<'tcx>, r_ty: Ty<'tcx>) -> bool {
             ty_str |
             ty_bare_fn(..) |
             ty_closure(_) |
-            ty_infer(_) |
-            ty_err |
             ty_param(_) |
             ty_projection(_) |
             ty_vec(_, None) => {
@@ -3656,9 +3677,12 @@ pub fn is_instantiable<'tcx>(cx: &ctxt<'tcx>, r_ty: Ty<'tcx>) -> bool {
                 r
             }
 
-            ty_unboxed_closure(did, _, substs) => {
-                let upvars = unboxed_closure_upvars(cx, did, substs).unwrap();
-                upvars.iter().any(|f| type_requires(cx, seen, r_ty, f.ty))
+            ty_err |
+            ty_infer(_) |
+            ty_unboxed_closure(..) => {
+                // this check is run on type definitions, so we don't expect to see
+                // inference by-products or unboxed closure types
+                cx.sess.bug(format!("requires check invoked on inapplicable type: {}", ty)[])
             }
 
             ty_tup(ref ts) => {
@@ -3748,9 +3772,10 @@ pub fn is_type_representable<'tcx>(cx: &ctxt<'tcx>, sp: Span, ty: Ty<'tcx>)
 
                 find_nonrepresentable(cx, sp, seen, iter)
             }
-            ty_unboxed_closure(did, _, substs) => {
-                let upvars = unboxed_closure_upvars(cx, did, substs).unwrap();
-                find_nonrepresentable(cx, sp, seen, upvars.iter().map(|f| f.ty))
+            ty_unboxed_closure(..) => {
+                // this check is run on type definitions, so we don't expect to see
+                // unboxed closure types
+                cx.sess.bug(format!("requires check invoked on inapplicable type: {}", ty)[])
             }
             _ => Representable,
         }
@@ -5708,7 +5733,10 @@ pub fn unboxed_closure_upvars<'tcx>(typer: &mc::Typer<'tcx>,
             freevars.iter()
                     .map(|freevar| {
                         let freevar_def_id = freevar.def.def_id();
-                        let freevar_ty = typer.node_ty(freevar_def_id.node);
+                        let freevar_ty = match typer.node_ty(freevar_def_id.node) {
+                            Ok(t) => { t }
+                            Err(()) => { return None; }
+                        };
                         let freevar_ty = freevar_ty.subst(tcx, substs);
 
                         match capture_mode {
@@ -6371,19 +6399,20 @@ impl Variance {
 
 /// Construct a parameter environment suitable for static contexts or other contexts where there
 /// are no free type/lifetime parameters in scope.
-pub fn empty_parameter_environment<'tcx>() -> ParameterEnvironment<'tcx> {
-    ty::ParameterEnvironment { free_substs: Substs::empty(),
+pub fn empty_parameter_environment<'a,'tcx>(cx: &'a ctxt<'tcx>) -> ParameterEnvironment<'a,'tcx> {
+    ty::ParameterEnvironment { tcx: cx,
+                               free_substs: Substs::empty(),
                                caller_bounds: GenericBounds::empty(),
                                implicit_region_bound: ty::ReEmpty,
                                selection_cache: traits::SelectionCache::new(), }
 }
 
 /// See `ParameterEnvironment` struct def'n for details
-pub fn construct_parameter_environment<'tcx>(
-    tcx: &ctxt<'tcx>,
+pub fn construct_parameter_environment<'a,'tcx>(
+    tcx: &'a ctxt<'tcx>,
     generics: &ty::Generics<'tcx>,
     free_id: ast::NodeId)
-    -> ParameterEnvironment<'tcx>
+    -> ParameterEnvironment<'a, 'tcx>
 {
 
     //
@@ -6426,6 +6455,7 @@ pub fn construct_parameter_environment<'tcx>(
            bounds.repr(tcx));
 
     return ty::ParameterEnvironment {
+        tcx: tcx,
         free_substs: free_substs,
         implicit_region_bound: ty::ReScope(free_id_scope),
         caller_bounds: bounds,
@@ -6516,57 +6546,76 @@ impl BorrowKind {
     }
 }
 
-impl<'tcx> mc::Typer<'tcx> for ty::ctxt<'tcx> {
-    fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> {
-        self
+impl<'tcx> ctxt<'tcx> {
+    pub fn capture_mode(&self, closure_expr_id: ast::NodeId)
+                    -> ast::CaptureClause {
+        self.capture_modes.borrow()[closure_expr_id].clone()
+    }
+
+    pub fn is_method_call(&self, expr_id: ast::NodeId) -> bool {
+        self.method_map.borrow().contains_key(&MethodCall::expr(expr_id))
+    }
+}
+
+impl<'a,'tcx> mc::Typer<'tcx> for ParameterEnvironment<'a,'tcx> {
+    fn tcx(&self) -> &ty::ctxt<'tcx> {
+        self.tcx
     }
 
-    fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> {
-        ty::node_id_to_type(self, id)
+    fn node_ty(&self, id: ast::NodeId) -> mc::McResult<Ty<'tcx>> {
+        Ok(ty::node_id_to_type(self.tcx, id))
     }
 
-    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> {
-        ty::expr_ty_adjusted(self, expr)
+    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> mc::McResult<Ty<'tcx>> {
+        Ok(ty::expr_ty_adjusted(self.tcx, expr))
     }
 
     fn node_method_ty(&self, method_call: ty::MethodCall) -> Option<Ty<'tcx>> {
-        self.method_map.borrow().get(&method_call).map(|method| method.ty)
+        self.tcx.method_map.borrow().get(&method_call).map(|method| method.ty)
     }
 
     fn node_method_origin(&self, method_call: ty::MethodCall)
                           -> Option<ty::MethodOrigin<'tcx>>
     {
-        self.method_map.borrow().get(&method_call).map(|method| method.origin.clone())
+        self.tcx.method_map.borrow().get(&method_call).map(|method| method.origin.clone())
     }
 
-    fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment<'tcx>>> {
-        &self.adjustments
+    fn adjustments(&self) -> &RefCell<NodeMap<ty::AutoAdjustment<'tcx>>> {
+        &self.tcx.adjustments
     }
 
     fn is_method_call(&self, id: ast::NodeId) -> bool {
-        self.method_map.borrow().contains_key(&MethodCall::expr(id))
+        self.tcx.is_method_call(id)
     }
 
     fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<region::CodeExtent> {
-        self.region_maps.temporary_scope(rvalue_id)
+        self.tcx.region_maps.temporary_scope(rvalue_id)
     }
 
     fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarBorrow> {
-        Some(self.upvar_borrow_map.borrow()[upvar_id].clone())
+        Some(self.tcx.upvar_borrow_map.borrow()[upvar_id].clone())
     }
 
     fn capture_mode(&self, closure_expr_id: ast::NodeId)
                     -> ast::CaptureClause {
-        self.capture_modes.borrow()[closure_expr_id].clone()
+        self.tcx.capture_mode(closure_expr_id)
+    }
+
+    fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool {
+        type_moves_by_default(self, span, ty)
     }
 }
 
-impl<'tcx> UnboxedClosureTyper<'tcx> for ty::ctxt<'tcx> {
+impl<'a,'tcx> UnboxedClosureTyper<'tcx> for ty::ParameterEnvironment<'a,'tcx> {
+    fn param_env<'b>(&'b self) -> &'b ty::ParameterEnvironment<'b,'tcx> {
+        self
+    }
+
     fn unboxed_closure_kind(&self,
                             def_id: ast::DefId)
                             -> ty::UnboxedClosureKind
     {
-        self.unboxed_closures.borrow()[def_id].kind
+        self.tcx.unboxed_closure_kind(def_id)
     }
 
     fn unboxed_closure_type(&self,
@@ -6574,7 +6623,7 @@ impl<'tcx> UnboxedClosureTyper<'tcx> for ty::ctxt<'tcx> {
                             substs: &subst::Substs<'tcx>)
                             -> ty::ClosureTy<'tcx>
     {
-        self.unboxed_closures.borrow()[def_id].closure_type.subst(self, substs)
+        self.tcx.unboxed_closure_type(def_id, substs)
     }
 
     fn unboxed_closure_upvars(&self,
@@ -6941,15 +6990,18 @@ pub enum CopyImplementationError {
     TypeIsStructural,
 }
 
-pub fn can_type_implement_copy<'tcx>(tcx: &ctxt<'tcx>,
-                                     self_type: Ty<'tcx>,
-                                     param_env: &ParameterEnvironment<'tcx>)
-                                     -> Result<(),CopyImplementationError> {
+pub fn can_type_implement_copy<'a,'tcx>(param_env: &ParameterEnvironment<'a, 'tcx>,
+                                        span: Span,
+                                        self_type: Ty<'tcx>)
+                                        -> Result<(),CopyImplementationError>
+{
+    let tcx = param_env.tcx;
+
     match self_type.sty {
         ty::ty_struct(struct_did, substs) => {
             let fields = ty::struct_fields(tcx, struct_did, substs);
             for field in fields.iter() {
-                if type_moves_by_default(tcx, field.mt.ty, param_env) {
+                if type_moves_by_default(param_env, span, field.mt.ty) {
                     return Err(FieldDoesNotImplementCopy(field.name))
                 }
             }
@@ -6960,9 +7012,7 @@ pub fn can_type_implement_copy<'tcx>(tcx: &ctxt<'tcx>,
                 for variant_arg_type in variant.args.iter() {
                     let substd_arg_type =
                         variant_arg_type.subst(tcx, substs);
-                    if type_moves_by_default(tcx,
-                                             substd_arg_type,
-                                             param_env) {
+                    if type_moves_by_default(param_env, span, substd_arg_type) {
                         return Err(VariantDoesNotImplementCopy(variant.name))
                     }
                 }
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index 2062685f4c8..17a3f4a88e5 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -87,7 +87,7 @@ struct CheckLoanCtxt<'a, 'tcx: 'a> {
     dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
     move_data: move_data::FlowedMoveData<'a, 'tcx>,
     all_loans: &'a [Loan<'tcx>],
-    param_env: &'a ty::ParameterEnvironment<'tcx>,
+    param_env: &'a ty::ParameterEnvironment<'a, 'tcx>,
 }
 
 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
@@ -208,9 +208,7 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
     };
 
     {
-        let mut euv = euv::ExprUseVisitor::new(&mut clcx,
-                                               bccx.tcx,
-                                               &param_env);
+        let mut euv = euv::ExprUseVisitor::new(&mut clcx, &param_env);
         euv.walk_fn(decl, body);
     }
 }
diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
index 7854c8acb47..1e9e5b22aa0 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
@@ -51,9 +51,7 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
     let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
 
     {
-        let mut euv = euv::ExprUseVisitor::new(&mut glcx,
-                                               bccx.tcx,
-                                               &param_env);
+        let mut euv = euv::ExprUseVisitor::new(&mut glcx, &param_env);
         euv.walk_fn(decl, body);
     }
 
@@ -485,13 +483,15 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
 /// This visitor walks static initializer's expressions and makes
 /// sure the loans being taken are sound.
 struct StaticInitializerCtxt<'a, 'tcx: 'a> {
-    bccx: &'a BorrowckCtxt<'a, 'tcx>
+    bccx: &'a BorrowckCtxt<'a, 'tcx>,
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> {
     fn visit_expr(&mut self, ex: &Expr) {
         if let ast::ExprAddrOf(mutbl, ref base) = ex.node {
-            let base_cmt = self.bccx.cat_expr(&**base);
+            let param_env = ty::empty_parameter_environment(self.bccx.tcx);
+            let mc = mc::MemCategorizationContext::new(&param_env);
+            let base_cmt = mc.cat_expr(&**base).unwrap();
             let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
             // Check that we don't allow borrows of unsafe static items.
             if check_aliasability(self.bccx, ex.span, euv::AddrOf,
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index c27b7b30e13..d81974d1ae0 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -511,14 +511,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
         self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
     }
 
-    pub fn mc(&self) -> mc::MemCategorizationContext<'a, ty::ctxt<'tcx>> {
-        mc::MemCategorizationContext::new(self.tcx)
-    }
-
-    pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt<'tcx> {
-        self.mc().cat_expr(expr)
-    }
-
     pub fn report(&self, err: BckError<'tcx>) {
         self.span_err(
             err.span,
@@ -526,13 +518,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
         self.note_and_explain_bckerr(err);
     }
 
-    pub fn report_use_of_moved_value(&self,
-                                     use_span: Span,
-                                     use_kind: MovedValueUseKind,
-                                     lp: &LoanPath<'tcx>,
-                                     the_move: &move_data::Move,
-                                     moved_lp: &LoanPath<'tcx>,
-                                     param_env: &ty::ParameterEnvironment<'tcx>) {
+    pub fn report_use_of_moved_value<'b>(&self,
+                                         use_span: Span,
+                                         use_kind: MovedValueUseKind,
+                                         lp: &LoanPath<'tcx>,
+                                         the_move: &move_data::Move,
+                                         moved_lp: &LoanPath<'tcx>,
+                                         param_env: &ty::ParameterEnvironment<'b,'tcx>) {
         let verb = match use_kind {
             MovedInUse => "use",
             MovedInCapture => "capture",
@@ -608,8 +600,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                                   r)[])
                     }
                 };
-                let (suggestion, _) = move_suggestion(self.tcx, param_env, expr_ty,
-                        ("moved by default", ""));
+                let (suggestion, _) =
+                    move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
                 self.tcx.sess.span_note(
                     expr_span,
                     format!("`{}` moved here{} because it has type `{}`, which is {}",
@@ -646,11 +638,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                                   r)[])
                     }
                 };
-                let (suggestion, help) = move_suggestion(self.tcx,
-                                                         param_env,
-                                                         expr_ty,
-                        ("moved by default", "make a copy and \
-                         capture that instead to override"));
+                let (suggestion, help) =
+                    move_suggestion(param_env,
+                                    expr_span,
+                                    expr_ty,
+                                    ("moved by default",
+                                     "make a copy and capture that instead to override"));
                 self.tcx.sess.span_note(
                     expr_span,
                     format!("`{}` moved into closure environment here{} because it \
@@ -663,22 +656,27 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             }
         }
 
-        fn move_suggestion<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                 param_env: &ty::ParameterEnvironment<'tcx>,
-                                 ty: Ty<'tcx>,
-                                 default_msgs: (&'static str, &'static str))
-                                 -> (&'static str, &'static str) {
+        fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
+                                    span: Span,
+                                    ty: Ty<'tcx>,
+                                    default_msgs: (&'static str, &'static str))
+                                    -> (&'static str, &'static str) {
             match ty.sty {
                 ty::ty_closure(box ty::ClosureTy {
-                        store: ty::RegionTraitStore(..),
-                        ..
-                    }) =>
+                    store: ty::RegionTraitStore(..),
+                    ..
+                }) => {
                     ("a non-copyable stack closure",
-                     "capture it in a new closure, e.g. `|x| f(x)`, to override"),
-                _ if ty::type_moves_by_default(tcx, ty, param_env) =>
-                    ("non-copyable",
-                     "perhaps you meant to use `clone()`?"),
-                _ => default_msgs,
+                     "capture it in a new closure, e.g. `|x| f(x)`, to override")
+                }
+                _ => {
+                    if ty::type_moves_by_default(param_env, span, ty) {
+                        ("non-copyable",
+                         "perhaps you meant to use `clone()`?")
+                    } else {
+                        default_msgs
+                    }
+                }
             }
         }
     }
@@ -991,7 +989,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
     }
 
     pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
-        self.mc().cmt_to_string(cmt)
+        cmt.descriptive_string(self.tcx)
     }
 }
 
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index fc68d1d3258..19781a51d57 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -542,7 +542,7 @@ fn enter_opt<'a, 'p, 'blk, 'tcx>(
             check_match::Constructor::Variant(def_id)
     };
 
-    let param_env = ty::empty_parameter_environment();
+    let param_env = ty::empty_parameter_environment(bcx.tcx());
     let mcx = check_match::MatchCheckCtxt {
         tcx: bcx.tcx(),
         param_env: param_env,
@@ -1008,7 +1008,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 
     let mcx = check_match::MatchCheckCtxt {
         tcx: bcx.tcx(),
-        param_env: ty::empty_parameter_environment(),
+        param_env: ty::empty_parameter_environment(bcx.tcx()),
     };
     let adt_vals = if any_irrefutable_adt_pat(bcx.tcx(), m, col) {
         let repr = adt::represent_type(bcx.ccx(), left_ty);
@@ -1262,8 +1262,7 @@ fn is_discr_reassigned(bcx: Block, discr: &ast::Expr, body: &ast::Expr) -> bool
         reassigned: false
     };
     {
-        let param_env = ty::empty_parameter_environment();
-        let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx, &param_env);
+        let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx);
         visitor.walk_expr(body);
     }
     rc.reassigned
@@ -1321,15 +1320,14 @@ fn create_bindings_map<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pat: &ast::Pat,
         let variable_ty = node_id_type(bcx, p_id);
         let llvariable_ty = type_of::type_of(ccx, variable_ty);
         let tcx = bcx.tcx();
-        let param_env = ty::empty_parameter_environment();
+        let param_env = ty::empty_parameter_environment(tcx);
 
         let llmatch;
         let trmode;
         match bm {
             ast::BindByValue(_)
-                if !ty::type_moves_by_default(tcx,
-                                              variable_ty,
-                                              &param_env) || reassigned => {
+                if !ty::type_moves_by_default(&param_env, span, variable_ty) || reassigned =>
+            {
                 llmatch = alloca_no_lifetime(bcx,
                                  llvariable_ty.ptr_to(),
                                  "__llmatch");
diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs
index 24a3bb42c90..85a7ec33069 100644
--- a/src/librustc_trans/trans/adt.rs
+++ b/src/librustc_trans/trans/adt.rs
@@ -61,7 +61,7 @@ use trans::datum;
 use trans::machine;
 use trans::type_::Type;
 use trans::type_of;
-use middle::ty::{mod, Ty};
+use middle::ty::{mod, Ty, UnboxedClosureTyper};
 use middle::ty::Disr;
 use syntax::ast;
 use syntax::attr;
@@ -168,7 +168,8 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             Univariant(mk_struct(cx, ftys[], packed, t), dtor)
         }
         ty::ty_unboxed_closure(def_id, _, substs) => {
-            let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id, substs).unwrap();
+            let typer = NormalizingUnboxedClosureTyper::new(cx.tcx());
+            let upvars = typer.unboxed_closure_upvars(def_id, substs).unwrap();
             let upvar_types = upvars.iter().map(|u| u.ty).collect::<Vec<_>>();
             Univariant(mk_struct(cx, upvar_types[], false, t), false)
         }
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index bf53885e9e5..4c29467d93a 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -731,7 +731,8 @@ pub fn iter_structural_ty<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
       }
       ty::ty_unboxed_closure(def_id, _, substs) => {
           let repr = adt::represent_type(cx.ccx(), t);
-          let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id, substs).unwrap();
+          let typer = common::NormalizingUnboxedClosureTyper::new(cx.tcx());
+          let upvars = typer.unboxed_closure_upvars(def_id, substs).unwrap();
           for (i, upvar) in upvars.iter().enumerate() {
               let llupvar = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i);
               cx = f(cx, llupvar, upvar.ty);
@@ -1451,6 +1452,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
           llfn: llfndecl,
           llenv: None,
           llretslotptr: Cell::new(None),
+          param_env: ty::empty_parameter_environment(ccx.tcx()),
           alloca_insert_pt: Cell::new(None),
           llreturn: Cell::new(None),
           needs_ret_allocas: nested_returns,
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index aa882240880..6efdcc2f0fa 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -302,6 +302,9 @@ pub struct FunctionContext<'a, 'tcx: 'a> {
     // section of the executable we're generating.
     pub llfn: ValueRef,
 
+    // always an empty parameter-environment
+    pub param_env: ty::ParameterEnvironment<'a, 'tcx>,
+
     // The environment argument in a closure.
     pub llenv: Option<ValueRef>,
 
@@ -579,12 +582,12 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> {
         self.tcx()
     }
 
-    fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> {
-        node_id_type(self, id)
+    fn node_ty(&self, id: ast::NodeId) -> mc::McResult<Ty<'tcx>> {
+        Ok(node_id_type(self, id))
     }
 
-    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> {
-        expr_ty_adjusted(self, expr)
+    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> mc::McResult<Ty<'tcx>> {
+        Ok(expr_ty_adjusted(self, expr))
     }
 
     fn node_method_ty(&self, method_call: ty::MethodCall) -> Option<Ty<'tcx>> {
@@ -625,9 +628,17 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> {
                     -> ast::CaptureClause {
         self.tcx().capture_modes.borrow()[closure_expr_id].clone()
     }
+
+    fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool {
+        self.fcx.param_env.type_moves_by_default(span, ty)
+    }
 }
 
 impl<'blk, 'tcx> ty::UnboxedClosureTyper<'tcx> for BlockS<'blk, 'tcx> {
+    fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx> {
+        &self.fcx.param_env
+    }
+
     fn unboxed_closure_kind(&self,
                             def_id: ast::DefId)
                             -> ty::UnboxedClosureKind
@@ -941,14 +952,10 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id());
     let infcx = infer::new_infer_ctxt(tcx);
 
-    // Parameter environment is used to give details about type parameters,
-    // but since we are in trans, everything is fully monomorphized.
-    let param_env = ty::empty_parameter_environment();
-
     // Do the initial selection for the obligation. This yields the
     // shallow result we are looking for -- that is, what specific impl.
-    let typer = NormalizingUnboxedClosureTyper::new(infcx.tcx);
-    let mut selcx = traits::SelectionContext::new(&infcx, &param_env, &typer);
+    let typer = NormalizingUnboxedClosureTyper::new(tcx);
+    let mut selcx = traits::SelectionContext::new(&infcx, &typer);
     let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
                                              trait_ref.to_poly_trait_predicate());
     let selection = match selcx.select(&obligation) {
@@ -983,7 +990,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let vtable = selection.map_move_nested(|predicate| {
         fulfill_cx.register_predicate_obligation(&infcx, predicate);
     });
-    let vtable = drain_fulfillment_cx(span, &infcx, &param_env, &mut fulfill_cx, &vtable);
+    let vtable = drain_fulfillment_cx(span, &infcx, &mut fulfill_cx, &vtable);
 
     info!("Cache miss: {}", trait_ref.repr(ccx.tcx()));
     ccx.trait_cache().borrow_mut().insert(trait_ref,
@@ -993,21 +1000,27 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 }
 
 pub struct NormalizingUnboxedClosureTyper<'a,'tcx:'a> {
-    tcx: &'a ty::ctxt<'tcx>
+    param_env: ty::ParameterEnvironment<'a, 'tcx>
 }
 
 impl<'a,'tcx> NormalizingUnboxedClosureTyper<'a,'tcx> {
     pub fn new(tcx: &'a ty::ctxt<'tcx>) -> NormalizingUnboxedClosureTyper<'a,'tcx> {
-        NormalizingUnboxedClosureTyper { tcx: tcx }
+        // Parameter environment is used to give details about type parameters,
+        // but since we are in trans, everything is fully monomorphized.
+        NormalizingUnboxedClosureTyper { param_env: ty::empty_parameter_environment(tcx) }
     }
 }
 
 impl<'a,'tcx> ty::UnboxedClosureTyper<'tcx> for NormalizingUnboxedClosureTyper<'a,'tcx> {
+    fn param_env<'b>(&'b self) -> &'b ty::ParameterEnvironment<'b,'tcx> {
+        &self.param_env
+    }
+
     fn unboxed_closure_kind(&self,
                             def_id: ast::DefId)
                             -> ty::UnboxedClosureKind
     {
-        self.tcx.unboxed_closure_kind(def_id)
+        self.param_env.tcx.unboxed_closure_kind(def_id)
     }
 
     fn unboxed_closure_type(&self,
@@ -1017,8 +1030,8 @@ impl<'a,'tcx> ty::UnboxedClosureTyper<'tcx> for NormalizingUnboxedClosureTyper<'
     {
         // the substitutions in `substs` are already monomorphized,
         // but we still must normalize associated types
-        let closure_ty = self.tcx.unboxed_closure_type(def_id, substs);
-        monomorphize::normalize_associated_type(self.tcx, &closure_ty)
+        let closure_ty = self.param_env.tcx.unboxed_closure_type(def_id, substs);
+        monomorphize::normalize_associated_type(self.param_env.tcx, &closure_ty)
     }
 
     fn unboxed_closure_upvars(&self,
@@ -1028,14 +1041,13 @@ impl<'a,'tcx> ty::UnboxedClosureTyper<'tcx> for NormalizingUnboxedClosureTyper<'
     {
         // the substitutions in `substs` are already monomorphized,
         // but we still must normalize associated types
-        let result = ty::unboxed_closure_upvars(self.tcx, def_id, substs);
-        monomorphize::normalize_associated_type(self.tcx, &result)
+        let result = ty::unboxed_closure_upvars(&self.param_env, def_id, substs);
+        monomorphize::normalize_associated_type(self.param_env.tcx, &result)
     }
 }
 
 pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span,
                                        infcx: &infer::InferCtxt<'a,'tcx>,
-                                       param_env: &ty::ParameterEnvironment<'tcx>,
                                        fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
                                        result: &T)
                                        -> T
@@ -1048,7 +1060,7 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span,
     // contains unbound type parameters. It could be a slight
     // optimization to stop iterating early.
     let typer = NormalizingUnboxedClosureTyper::new(infcx.tcx);
-    match fulfill_cx.select_all_or_error(infcx, param_env, &typer) {
+    match fulfill_cx.select_all_or_error(infcx, &typer) {
         Ok(()) => { }
         Err(errors) => {
             if errors.iter().all(|e| e.is_overflow()) {
diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs
index 83bf06383a8..72074040a2c 100644
--- a/src/librustc_trans/trans/datum.rs
+++ b/src/librustc_trans/trans/datum.rs
@@ -28,6 +28,7 @@ use util::ppaux::{ty_to_string};
 
 use std::fmt;
 use syntax::ast;
+use syntax::codemap::DUMMY_SP;
 
 /// A `Datum` encapsulates the result of evaluating an expression.  It
 /// describes where the value is stored, what Rust type the value has,
@@ -543,8 +544,9 @@ impl<'tcx, K: KindOps + fmt::Show> Datum<'tcx, K> {
          * affine values (since they must never be duplicated).
          */
 
-        let param_env = ty::empty_parameter_environment();
-        assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty, &param_env));
+        assert!(!ty::type_moves_by_default(&ty::empty_parameter_environment(bcx.tcx()),
+                                           DUMMY_SP,
+                                           self.ty));
         self.shallow_copy_raw(bcx, dst)
     }
 
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index 7c8ba08d987..cc259e6765c 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -322,9 +322,8 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T
     // FIXME(#20304) -- cache
 
     let infcx = infer::new_infer_ctxt(tcx);
-    let param_env = ty::empty_parameter_environment();
-    let typer = NormalizingUnboxedClosureTyper::new(infcx.tcx);
-    let mut selcx = traits::SelectionContext::new(&infcx, &param_env, &typer);
+    let typer = NormalizingUnboxedClosureTyper::new(tcx);
+    let mut selcx = traits::SelectionContext::new(&infcx, &typer);
     let cause = traits::ObligationCause::dummy();
     let traits::Normalized { value: result, obligations } =
         traits::normalize(&mut selcx, cause, value);
@@ -337,7 +336,7 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T
     for obligation in obligations.into_iter() {
         fulfill_cx.register_predicate_obligation(&infcx, obligation);
     }
-    let result = drain_fulfillment_cx(DUMMY_SP, &infcx, &param_env, &mut fulfill_cx, &result);
+    let result = drain_fulfillment_cx(DUMMY_SP, &infcx, &mut fulfill_cx, &result);
 
     result
 }
diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs
index 5e9843d0e00..6950850e5f3 100644
--- a/src/librustc_typeck/check/assoc.rs
+++ b/src/librustc_typeck/check/assoc.rs
@@ -18,7 +18,6 @@ use syntax::codemap::Span;
 use util::ppaux::Repr;
 
 pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
-                                                param_env: &ty::ParameterEnvironment<'tcx>,
                                                 typer: &(ty::UnboxedClosureTyper<'tcx>+'a),
                                                 fulfillment_cx: &mut FulfillmentContext<'tcx>,
                                                 span: Span,
@@ -28,7 +27,7 @@ pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
     where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone + Repr<'tcx>
 {
     debug!("normalize_associated_types_in(value={})", value.repr(infcx.tcx));
-    let mut selcx = SelectionContext::new(infcx, param_env, typer);
+    let mut selcx = SelectionContext::new(infcx, typer);
     let cause = ObligationCause::new(span, body_id, MiscObligation);
     let Normalized { value: result, obligations } = traits::normalize(&mut selcx, cause, value);
     debug!("normalize_associated_types_in: result={} predicates={}",
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 4e6593deddd..92b8c2bbcf7 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -169,9 +169,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
                                               poly_trait_ref.as_predicate());
 
     // Now we want to know if this can be matched
-    let mut selcx = traits::SelectionContext::new(fcx.infcx(),
-                                                  &fcx.inh.param_env,
-                                                  fcx);
+    let mut selcx = traits::SelectionContext::new(fcx.infcx(), fcx);
     if !selcx.evaluate_obligation(&obligation) {
         debug!("--> Cannot match obligation");
         return None; // Cannot be matched, no such method resolution is possible.
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 52860abb6f9..cd97d89b246 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -788,9 +788,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                     debug!("impl_obligations={}", obligations.repr(self.tcx()));
 
                     // Evaluate those obligations to see if they might possibly hold.
-                    let mut selcx = traits::SelectionContext::new(self.infcx(),
-                                                                  &self.fcx.inh.param_env,
-                                                                  self.fcx);
+                    let mut selcx = traits::SelectionContext::new(self.infcx(), self.fcx);
                     obligations.all(|o| selcx.evaluate_obligation(o))
                 }
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 19ec85dc61e..82525b71052 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -87,6 +87,7 @@ use middle::{const_eval, def};
 use middle::infer;
 use middle::lang_items::IteratorItem;
 use middle::mem_categorization as mc;
+use middle::mem_categorization::McResult;
 use middle::pat_util::{mod, pat_id_map};
 use middle::region::CodeExtent;
 use middle::subst::{mod, Subst, Substs, VecPerParamSpace, ParamSpace};
@@ -129,6 +130,7 @@ pub mod regionmanip;
 pub mod regionck;
 pub mod demand;
 pub mod method;
+mod upvar;
 pub mod wf;
 mod closure;
 mod callee;
@@ -146,7 +148,7 @@ mod callee;
 pub struct Inherited<'a, 'tcx: 'a> {
     infcx: infer::InferCtxt<'a, 'tcx>,
     locals: RefCell<NodeMap<Ty<'tcx>>>,
-    param_env: ty::ParameterEnvironment<'tcx>,
+    param_env: ty::ParameterEnvironment<'a, 'tcx>,
 
     // Temporary tables:
     node_types: RefCell<NodeMap<Ty<'tcx>>>,
@@ -288,13 +290,17 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> {
     fn tcx(&self) -> &ty::ctxt<'tcx> {
         self.ccx.tcx
     }
-    fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> {
+    fn node_ty(&self, id: ast::NodeId) -> McResult<Ty<'tcx>> {
         let ty = self.node_ty(id);
-        self.infcx().resolve_type_vars_if_possible(&ty)
+        self.resolve_type_vars_or_error(&ty)
     }
-    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> {
-        let ty = self.expr_ty_adjusted(expr);
-        self.infcx().resolve_type_vars_if_possible(&ty)
+    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult<Ty<'tcx>> {
+        let ty = self.adjust_expr_ty(expr, self.inh.adjustments.borrow().get(&expr.id));
+        self.resolve_type_vars_or_error(&ty)
+    }
+    fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool {
+        let ty = self.infcx().resolve_type_vars_if_possible(&ty);
+        traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span)
     }
     fn node_method_ty(&self, method_call: ty::MethodCall)
                       -> Option<Ty<'tcx>> {
@@ -317,7 +323,7 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> {
         self.inh.method_map.borrow().contains_key(&ty::MethodCall::expr(id))
     }
     fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<CodeExtent> {
-        self.tcx().temporary_scope(rvalue_id)
+        self.param_env().temporary_scope(rvalue_id)
     }
     fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarBorrow> {
         self.inh.upvar_borrow_map.borrow().get(&upvar_id).cloned()
@@ -329,6 +335,10 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> ty::UnboxedClosureTyper<'tcx> for FnCtxt<'a, 'tcx> {
+    fn param_env<'b>(&'b self) -> &'b ty::ParameterEnvironment<'b,'tcx> {
+        &self.inh.param_env
+    }
+
     fn unboxed_closure_kind(&self,
                             def_id: ast::DefId)
                             -> ty::UnboxedClosureKind
@@ -355,7 +365,7 @@ impl<'a, 'tcx> ty::UnboxedClosureTyper<'tcx> for FnCtxt<'a, 'tcx> {
 
 impl<'a, 'tcx> Inherited<'a, 'tcx> {
     fn new(tcx: &'a ty::ctxt<'tcx>,
-           param_env: ty::ParameterEnvironment<'tcx>)
+           param_env: ty::ParameterEnvironment<'a, 'tcx>)
            -> Inherited<'a, 'tcx> {
         Inherited {
             infcx: infer::new_infer_ctxt(tcx),
@@ -383,7 +393,6 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
     {
         let mut fulfillment_cx = self.fulfillment_cx.borrow_mut();
         assoc::normalize_associated_types_in(&self.infcx,
-                                             &self.param_env,
                                              typer,
                                              &mut *fulfillment_cx, span,
                                              body_id,
@@ -413,7 +422,7 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>)
                                     -> Inherited<'a, 'tcx> {
     // 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
-    let param_env = ty::empty_parameter_environment();
+    let param_env = ty::empty_parameter_environment(ccx.tcx);
     Inherited::new(ccx.tcx, param_env)
 }
 
@@ -457,7 +466,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                            body: &ast::Block,
                            id: ast::NodeId,
                            raw_fty: Ty<'tcx>,
-                           param_env: ty::ParameterEnvironment<'tcx>) {
+                           param_env: ty::ParameterEnvironment<'a, 'tcx>) {
     match raw_fty.sty {
         ty::ty_bare_fn(_, ref fn_ty) => {
             let inh = Inherited::new(ccx.tcx, param_env);
@@ -468,12 +477,13 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
             let fn_sig =
                 liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig);
             let fn_sig =
-                inh.normalize_associated_types_in(ccx.tcx, body.span, body.id, &fn_sig);
+                inh.normalize_associated_types_in(&inh.param_env, body.span, body.id, &fn_sig);
 
             let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig,
                                decl, id, body, &inh);
 
             vtable::select_all_fcx_obligations_or_error(&fcx);
+            upvar::closure_analyze_fn(&fcx, id, decl, body);
             regionck::regionck_fn(&fcx, id, decl, body);
             writeback::resolve_type_vars_in_fn(&fcx, decl, body);
         }
@@ -1220,7 +1230,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
         let impl_sig =
             assoc::normalize_associated_types_in(&infcx,
                                                  &impl_param_env,
-                                                 infcx.tcx,
                                                  &mut fulfillment_cx,
                                                  impl_m_span,
                                                  impl_m_body_id,
@@ -1241,7 +1250,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
         let trait_sig =
             assoc::normalize_associated_types_in(&infcx,
                                                  &impl_param_env,
-                                                 infcx.tcx,
                                                  &mut fulfillment_cx,
                                                  impl_m_span,
                                                  impl_m_body_id,
@@ -1277,7 +1285,7 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
 
     // Run the fulfillment context to completion to accommodate any
     // associated type normalizations that may have occurred.
-    match fulfillment_cx.select_all_or_error(&infcx, &impl_param_env, tcx) {
+    match fulfillment_cx.select_all_or_error(&infcx, &impl_param_env) {
         Ok(()) => { }
         Err(errors) => {
             traits::report_fulfillment_errors(&infcx, &errors);
@@ -1457,7 +1465,7 @@ fn check_cast(fcx: &FnCtxt,
         return
     }
 
-    if !fcx.type_is_known_to_be_sized(t_1) {
+    if !fcx.type_is_known_to_be_sized(t_1, cast_expr.span) {
         let tstr = fcx.infcx().ty_to_string(t_1);
         fcx.type_error_message(span, |actual| {
             format!("cast to unsized type: `{}` as `{}`", actual, tstr)
@@ -1655,11 +1663,11 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn tcx(&self) -> &ty::ctxt<'tcx> { self.ccx.tcx }
 
-    pub fn infcx(&self) -> &infer::InferCtxt<'a, 'tcx> {
+    pub fn infcx(&self) -> &infer::InferCtxt<'a,'tcx> {
         &self.inh.infcx
     }
 
-    pub fn param_env(&self) -> &ty::ParameterEnvironment<'tcx> {
+    pub fn param_env(&self) -> &ty::ParameterEnvironment<'a,'tcx> {
         &self.inh.param_env
     }
 
@@ -1671,6 +1679,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.ccx.tcx.sess.err_count() - self.err_count_on_creation
     }
 
+    /// Resolves all type variables in `t` and then, if any were left
+    /// unresolved, substitutes an error type. This is used after the
+    /// main checking when doing a second pass before writeback. The
+    /// justification is that writeback will produce an error for
+    /// these unconstrained type variables.
+    fn resolve_type_vars_or_error(&self, t: &Ty<'tcx>) -> mc::McResult<Ty<'tcx>> {
+        let t = self.infcx().resolve_type_vars_if_possible(t);
+        if ty::type_has_ty_infer(t) || ty::type_is_error(t) { Err(()) } else { Ok(t) }
+    }
+
     pub fn tag(&self) -> String {
         format!("{}", self as *const FnCtxt)
     }
@@ -1820,7 +1838,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.inh.fulfillment_cx
             .borrow_mut()
             .normalize_projection_type(self.infcx(),
-                                       &self.inh.param_env,
                                        self,
                                        ty::ProjectionTy {
                                            trait_ref: trait_ref,
@@ -1966,13 +1983,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     pub fn type_is_known_to_be_sized(&self,
-                                     ty: Ty<'tcx>)
+                                     ty: Ty<'tcx>,
+                                     span: Span)
                                      -> bool
     {
         traits::type_known_to_meet_builtin_bound(self.infcx(),
                                                  self.param_env(),
                                                  ty,
-                                                 ty::BoundSized)
+                                                 ty::BoundSized,
+                                                 span)
     }
 
     pub fn register_builtin_bound(&self,
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index d01b79068aa..e97a29ec458 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -81,38 +81,6 @@
 //! traversed.  This is essentially the same as the ownership
 //! relation, except that a borrowed pointer never owns its
 //! contents.
-//!
-//! ### Inferring borrow kinds for upvars
-//!
-//! Whenever there is a closure expression, we need to determine how each
-//! upvar is used. We do this by initially assigning each upvar an
-//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then
-//! "escalating" the kind as needed. The borrow kind proceeds according to
-//! the following lattice:
-//!
-//!     ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
-//!
-//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
-//! will promote its borrow kind to mutable borrow. If we see an `&mut x`
-//! we'll do the same. Naturally, this applies not just to the upvar, but
-//! to everything owned by `x`, so the result is the same for something
-//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
-//! struct). These adjustments are performed in
-//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
-//! from there).
-//!
-//! The fact that we are inferring borrow kinds as we go results in a
-//! semi-hacky interaction with mem-categorization. In particular,
-//! mem-categorization will query the current borrow kind as it
-//! categorizes, and we'll return the *current* value, but this may get
-//! adjusted later. Therefore, in this module, we generally ignore the
-//! borrow kind (and derived mutabilities) that are returned from
-//! mem-categorization, since they may be inaccurate. (Another option
-//! would be to use a unification scheme, where instead of returning a
-//! concrete borrow kind like `ty::ImmBorrow`, we return a
-//! `ty::InferBorrow(upvar_id)` or something like that, but this would
-//! then mean that all later passes would have to check for these figments
-//! and report an error, and it just seems like more mess in the end.)
 
 use astconv::AstConv;
 use check::FnCtxt;
@@ -126,7 +94,6 @@ use middle::ty::{ReScope};
 use middle::ty::{mod, Ty, MethodCall};
 use middle::infer;
 use middle::pat_util;
-use util::nodemap::{FnvHashMap};
 use util::ppaux::{ty_to_string, Repr};
 
 use syntax::{ast, ast_util};
@@ -134,12 +101,13 @@ use syntax::codemap::Span;
 use syntax::visit;
 use syntax::visit::Visitor;
 
-use std::cell::{RefCell};
-use std::collections::hash_map::Entry::{Vacant, Occupied};
-
 use self::RepeatingScope::Repeating;
 use self::SubjectNode::Subject;
 
+// a variation on try that just returns unit
+macro_rules! ignore_err {
+    ($e:expr) => (match $e { Ok(e) => e, Err(_) => return () })
+}
 
 ///////////////////////////////////////////////////////////////////////////
 // PUBLIC ENTRY POINTS
@@ -193,19 +161,6 @@ pub fn regionck_ensure_component_tys_wf<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 ///////////////////////////////////////////////////////////////////////////
 // INTERNALS
 
-// Stores parameters for a potential call to link_region()
-// to perform if an upvar reference is marked unique/mutable after
-// it has already been processed before.
-struct MaybeLink<'tcx> {
-    span: Span,
-    borrow_region: ty::Region,
-    borrow_kind: ty::BorrowKind,
-    borrow_cmt: mc::cmt<'tcx>
-}
-
-// A map associating an upvar ID to a vector of the above
-type MaybeLinkMap<'tcx> = RefCell<FnvHashMap<ty::UpvarId, Vec<MaybeLink<'tcx>>>>;
-
 pub struct Rcx<'a, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'tcx>,
 
@@ -216,10 +171,6 @@ pub struct Rcx<'a, 'tcx: 'a> {
 
     // id of AST node being analyzed (the subject of the analysis).
     subject: SubjectNode,
-
-    // Possible region links we will establish if an upvar
-    // turns out to be unique/mutable
-    maybe_links: MaybeLinkMap<'tcx>
 }
 
 /// Returns the validity region of `def` -- that is, how long is `def` valid?
@@ -254,8 +205,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
         Rcx { fcx: fcx,
               repeating_scope: initial_repeating_scope,
               subject: subject,
-              region_param_pairs: Vec::new(),
-              maybe_links: RefCell::new(FnvHashMap::new()) }
+              region_param_pairs: Vec::new() }
     }
 
     pub fn tcx(&self) -> &'a ty::ctxt<'tcx> {
@@ -586,19 +536,12 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
             visit::walk_expr(rcx, expr);
         }
 
-        ast::ExprAssign(ref lhs, _) => {
-            adjust_borrow_kind_for_assignment_lhs(rcx, &**lhs);
-            visit::walk_expr(rcx, expr);
-        }
-
         ast::ExprAssignOp(_, ref lhs, ref rhs) => {
             if has_method_map {
                 constrain_call(rcx, expr, Some(&**lhs),
                                Some(&**rhs).into_iter(), true);
             }
 
-            adjust_borrow_kind_for_assignment_lhs(rcx, &**lhs);
-
             visit::walk_expr(rcx, expr);
         }
 
@@ -835,22 +778,6 @@ fn check_expr_fn_block(rcx: &mut Rcx,
     rcx.set_repeating_scope(repeating_scope);
 
     match function_type.sty {
-        ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(..), .. }) => {
-            ty::with_freevars(tcx, expr.id, |freevars| {
-                propagate_upupvar_borrow_kind(rcx, expr, freevars);
-            })
-        }
-        ty::ty_unboxed_closure(..) => {
-            if tcx.capture_modes.borrow()[expr.id].clone() == ast::CaptureByRef {
-                ty::with_freevars(tcx, expr.id, |freevars| {
-                    propagate_upupvar_borrow_kind(rcx, expr, freevars);
-                });
-            }
-        }
-        _ => {}
-    }
-
-    match function_type.sty {
         ty::ty_closure(box ty::ClosureTy {ref bounds, ..}) => {
             ty::with_freevars(tcx, expr.id, |freevars| {
                 ensure_free_variable_types_outlive_closure_bound(rcx, bounds, expr, freevars);
@@ -926,7 +853,6 @@ fn check_expr_fn_block(rcx: &mut Rcx,
         freevars: &[ty::Freevar])
     {
         let tcx = rcx.fcx.ccx.tcx;
-        let infcx = rcx.fcx.infcx();
         debug!("constrain_free_variables({}, {})",
                region_bound.repr(tcx), expr.repr(tcx));
         for freevar in freevars.iter() {
@@ -942,20 +868,10 @@ fn check_expr_fn_block(rcx: &mut Rcx,
             let upvar_id = ty::UpvarId { var_id: var_node_id,
                                          closure_expr_id: expr.id };
 
-            // Create a region variable to represent this borrow. This borrow
-            // must outlive the region on the closure.
-            let origin = infer::UpvarRegion(upvar_id, expr.span);
-            let freevar_region = infcx.next_region_var(origin);
-            rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id),
-                            region_bound, freevar_region);
+            let upvar_borrow = rcx.fcx.inh.upvar_borrow_map.borrow()[upvar_id];
 
-            // Create a UpvarBorrow entry. Note that we begin with a
-            // const borrow_kind, but change it to either mut or
-            // immutable as dictated by the uses.
-            let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
-                                                 region: freevar_region };
-            rcx.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id,
-                                                             upvar_borrow);
+            rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id),
+                            region_bound, upvar_borrow.region);
 
             // Guarantee that the closure does not outlive the variable itself.
             let enclosing_region = region_of_def(rcx.fcx, def);
@@ -964,52 +880,6 @@ fn check_expr_fn_block(rcx: &mut Rcx,
                             region_bound, enclosing_region);
         }
     }
-
-    fn propagate_upupvar_borrow_kind(rcx: &mut Rcx,
-                                     expr: &ast::Expr,
-                                     freevars: &[ty::Freevar]) {
-        let tcx = rcx.fcx.ccx.tcx;
-        debug!("propagate_upupvar_borrow_kind({})", expr.repr(tcx));
-        for freevar in freevars.iter() {
-            // Because of the semi-hokey way that we are doing
-            // borrow_kind inference, we need to check for
-            // indirect dependencies, like so:
-            //
-            //     let mut x = 0;
-            //     outer_call(|| {
-            //         inner_call(|| {
-            //             x = 1;
-            //         });
-            //     });
-            //
-            // Here, the `inner_call` is basically "reborrowing" the
-            // outer pointer. With no other changes, `inner_call`
-            // would infer that it requires a mutable borrow, but
-            // `outer_call` would infer that a const borrow is
-            // sufficient. This is because we haven't linked the
-            // borrow_kind of the borrow that occurs in the inner
-            // closure to the borrow_kind of the borrow in the outer
-            // closure. Note that regions *are* naturally linked
-            // because we have a proper inference scheme there.
-            //
-            // Anyway, for borrow_kind, we basically go back over now
-            // after checking the inner closure (and hence
-            // determining the final borrow_kind) and propagate that as
-            // a constraint on the outer closure.
-            if let def::DefUpvar(var_id, outer_closure_id, _) = freevar.def {
-                // thing being captured is itself an upvar:
-                let outer_upvar_id = ty::UpvarId {
-                    var_id: var_id,
-                    closure_expr_id: outer_closure_id };
-                let inner_upvar_id = ty::UpvarId {
-                    var_id: var_id,
-                    closure_expr_id: expr.id };
-                link_upvar_borrow_kind_for_nested_closures(rcx,
-                                                           inner_upvar_id,
-                                                           outer_upvar_id);
-            }
-        }
-    }
 }
 
 fn constrain_callee(rcx: &mut Rcx,
@@ -1027,7 +897,7 @@ fn constrain_callee(rcx: &mut Rcx,
                     // While we're here, link the closure's region with a unique
                     // immutable borrow (gathered later in borrowck)
                     let mc = mc::MemCategorizationContext::new(rcx.fcx);
-                    let expr_cmt = mc.cat_expr(callee_expr);
+                    let expr_cmt = ignore_err!(mc.cat_expr(callee_expr));
                     link_region(rcx, callee_expr.span, call_region,
                                 ty::UniqueImmBorrow, expr_cmt);
                     r
@@ -1136,7 +1006,7 @@ fn constrain_autoderefs<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
                 };
                 {
                     let mc = mc::MemCategorizationContext::new(rcx.fcx);
-                    let self_cmt = mc.cat_expr_autoderefd(deref_expr, i);
+                    let self_cmt = ignore_err!(mc.cat_expr_autoderefd(deref_expr, i));
                     link_region(rcx, deref_expr.span, *r,
                                 ty::BorrowKind::from_mutbl(m), self_cmt);
                 }
@@ -1232,7 +1102,7 @@ fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr,
 
     let cmt = {
         let mc = mc::MemCategorizationContext::new(rcx.fcx);
-        mc.cat_expr(base)
+        ignore_err!(mc.cat_expr(base))
     };
     link_region_from_node_type(rcx, expr.span, expr.id, mutability, cmt);
 }
@@ -1247,7 +1117,7 @@ fn link_local(rcx: &Rcx, local: &ast::Local) {
         Some(ref expr) => &**expr,
     };
     let mc = mc::MemCategorizationContext::new(rcx.fcx);
-    let discr_cmt = mc.cat_expr(init_expr);
+    let discr_cmt = ignore_err!(mc.cat_expr(init_expr));
     link_pattern(rcx, mc, discr_cmt, &*local.pat);
 }
 
@@ -1257,7 +1127,7 @@ fn link_local(rcx: &Rcx, local: &ast::Local) {
 fn link_match(rcx: &Rcx, discr: &ast::Expr, arms: &[ast::Arm]) {
     debug!("regionck::for_match()");
     let mc = mc::MemCategorizationContext::new(rcx.fcx);
-    let discr_cmt = mc.cat_expr(discr);
+    let discr_cmt = ignore_err!(mc.cat_expr(discr));
     debug!("discr_cmt={}", discr_cmt.repr(rcx.tcx()));
     for arm in arms.iter() {
         for root_pat in arm.pats.iter() {
@@ -1303,11 +1173,14 @@ fn link_pattern<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
 
                 // `[_, ..slice, _]` pattern
                 ast::PatVec(_, Some(ref slice_pat), _) => {
-                    let (slice_cmt, slice_mutbl, slice_r) =
-                        mc.cat_slice_pattern(sub_cmt, &**slice_pat);
-                    link_region(rcx, sub_pat.span, slice_r,
-                                ty::BorrowKind::from_mutbl(slice_mutbl),
-                                slice_cmt);
+                    match mc.cat_slice_pattern(sub_cmt, &**slice_pat) {
+                        Ok((slice_cmt, slice_mutbl, slice_r)) => {
+                            link_region(rcx, sub_pat.span, slice_r,
+                                        ty::BorrowKind::from_mutbl(slice_mutbl),
+                                        slice_cmt);
+                        }
+                        Err(()) => {}
+                    }
                 }
                 _ => {}
             }
@@ -1323,7 +1196,7 @@ fn link_autoref(rcx: &Rcx,
 
     debug!("link_autoref(autoref={})", autoref);
     let mc = mc::MemCategorizationContext::new(rcx.fcx);
-    let expr_cmt = mc.cat_expr_autoderefd(expr, autoderefs);
+    let expr_cmt = ignore_err!(mc.cat_expr_autoderefd(expr, autoderefs));
     debug!("expr_cmt={}", expr_cmt.repr(rcx.tcx()));
 
     match *autoref {
@@ -1345,7 +1218,7 @@ fn link_by_ref(rcx: &Rcx,
     debug!("link_by_ref(expr={}, callee_scope={})",
            expr.repr(tcx), callee_scope);
     let mc = mc::MemCategorizationContext::new(rcx.fcx);
-    let expr_cmt = mc.cat_expr(expr);
+    let expr_cmt = ignore_err!(mc.cat_expr(expr));
     let borrow_region = ty::ReScope(callee_scope);
     link_region(rcx, expr.span, borrow_region, ty::ImmBorrow, expr_cmt);
 }
@@ -1484,14 +1357,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
                 rcx.fcx.inh.upvar_borrow_map.borrow_mut();
             match upvar_borrow_map.get_mut(upvar_id) {
                 Some(upvar_borrow) => {
-                    // Adjust mutability that we infer for the upvar
-                    // so it can accommodate being borrowed with
-                    // mutability `kind`:
-                    adjust_upvar_borrow_kind_for_loan(rcx,
-                                                      *upvar_id,
-                                                      upvar_borrow,
-                                                      borrow_kind);
-
                     // The mutability of the upvar may have been modified
                     // by the above adjustment, so update our local variable.
                     ref_kind = upvar_borrow.kind;
@@ -1576,27 +1441,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
             // else the user is borrowed imm memory as mut memory,
             // which means they'll get an error downstream in borrowck
             // anyhow.)
-            //
-            // If mutability was inferred from an upvar, we may be
-            // forced to revisit this decision later if processing
-            // another borrow or nested closure ends up converting the
-            // upvar borrow kind to mutable/unique.  Record the
-            // information needed to perform the recursive link in the
-            // maybe link map.
-            if let mc::NoteUpvarRef(upvar_id) = note {
-                let link = MaybeLink {
-                    span: span,
-                    borrow_region: borrow_region,
-                    borrow_kind: new_borrow_kind,
-                    borrow_cmt: ref_cmt
-                };
-
-                match rcx.maybe_links.borrow_mut().entry(upvar_id) {
-                    Vacant(entry) => { entry.set(vec![link]); }
-                    Occupied(entry) => { entry.into_mut().push(link); }
-                }
-            }
-
             return None;
         }
 
@@ -1608,178 +1452,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
     }
 }
 
-/// Adjusts the inferred borrow_kind as needed to account for upvars that are assigned to in an
-/// assignment expression.
-fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx,
-                                         lhs: &ast::Expr) {
-    let mc = mc::MemCategorizationContext::new(rcx.fcx);
-    let cmt = mc.cat_expr(lhs);
-    adjust_upvar_borrow_kind_for_mut(rcx, cmt);
-}
-
-/// Indicates that `cmt` is being directly mutated (e.g., assigned to). If cmt contains any by-ref
-/// upvars, this implies that those upvars must be borrowed using an `&mut` borow.
-fn adjust_upvar_borrow_kind_for_mut<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
-                                              cmt: mc::cmt<'tcx>) {
-    let mut cmt = cmt;
-    loop {
-        debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
-               cmt.repr(rcx.tcx()));
-
-        match cmt.cat.clone() {
-            mc::cat_deref(base, _, mc::Unique) |
-            mc::cat_interior(base, _) |
-            mc::cat_downcast(base, _) => {
-                // Interior or owned data is mutable if base is
-                // mutable, so iterate to the base.
-                cmt = base;
-                continue;
-            }
-
-            mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
-            mc::cat_deref(base, _, mc::Implicit(..)) => {
-                if let mc::NoteUpvarRef(ref upvar_id) = cmt.note {
-                    // if this is an implicit deref of an
-                    // upvar, then we need to modify the
-                    // borrow_kind of the upvar to make sure it
-                    // is inferred to mutable if necessary
-                    let mut upvar_borrow_map =
-                        rcx.fcx.inh.upvar_borrow_map.borrow_mut();
-                    let ub = &mut (*upvar_borrow_map)[*upvar_id];
-                    return adjust_upvar_borrow_kind(rcx, *upvar_id, ub, ty::MutBorrow);
-                }
-
-                // assignment to deref of an `&mut`
-                // borrowed pointer implies that the
-                // pointer itself must be unique, but not
-                // necessarily *mutable*
-                return adjust_upvar_borrow_kind_for_unique(rcx, base);
-            }
-
-            mc::cat_deref(_, _, mc::UnsafePtr(..)) |
-            mc::cat_static_item |
-            mc::cat_rvalue(_) |
-            mc::cat_local(_) |
-            mc::cat_upvar(..) => {
-                return;
-            }
-        }
-    }
-}
-
-fn adjust_upvar_borrow_kind_for_unique<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, cmt: mc::cmt<'tcx>) {
-    let mut cmt = cmt;
-    loop {
-        debug!("adjust_upvar_borrow_kind_for_unique(cmt={})",
-               cmt.repr(rcx.tcx()));
-
-        match cmt.cat.clone() {
-            mc::cat_deref(base, _, mc::Unique) |
-            mc::cat_interior(base, _) |
-            mc::cat_downcast(base, _) => {
-                // Interior or owned data is unique if base is
-                // unique.
-                cmt = base;
-                continue;
-            }
-
-            mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
-            mc::cat_deref(base, _, mc::Implicit(..)) => {
-                if let mc::NoteUpvarRef(ref upvar_id) = cmt.note {
-                    // if this is an implicit deref of an
-                    // upvar, then we need to modify the
-                    // borrow_kind of the upvar to make sure it
-                    // is inferred to unique if necessary
-                    let mut ub = rcx.fcx.inh.upvar_borrow_map.borrow_mut();
-                    let ub = &mut (*ub)[*upvar_id];
-                    return adjust_upvar_borrow_kind(rcx, *upvar_id, ub, ty::UniqueImmBorrow);
-                }
-
-                // for a borrowed pointer to be unique, its
-                // base must be unique
-                return adjust_upvar_borrow_kind_for_unique(rcx, base);
-            }
-
-            mc::cat_deref(_, _, mc::UnsafePtr(..)) |
-            mc::cat_static_item |
-            mc::cat_rvalue(_) |
-            mc::cat_local(_) |
-            mc::cat_upvar(..) => {
-                return;
-            }
-        }
-    }
-}
-
-/// Indicates that the borrow_kind of `outer_upvar_id` must permit a reborrowing with the
-/// borrow_kind of `inner_upvar_id`. This occurs in nested closures, see comment above at the call
-/// to this function.
-fn link_upvar_borrow_kind_for_nested_closures(rcx: &mut Rcx,
-                                              inner_upvar_id: ty::UpvarId,
-                                              outer_upvar_id: ty::UpvarId) {
-    debug!("link_upvar_borrow_kind: inner_upvar_id={} outer_upvar_id={}",
-           inner_upvar_id, outer_upvar_id);
-
-    let mut upvar_borrow_map = rcx.fcx.inh.upvar_borrow_map.borrow_mut();
-    let inner_borrow = upvar_borrow_map[inner_upvar_id].clone();
-    match upvar_borrow_map.get_mut(&outer_upvar_id) {
-        Some(outer_borrow) => {
-            adjust_upvar_borrow_kind(rcx, outer_upvar_id, outer_borrow, inner_borrow.kind);
-        }
-        None => { /* outer closure is not a stack closure */ }
-    }
-}
-
-fn adjust_upvar_borrow_kind_for_loan(rcx: &Rcx,
-                                     upvar_id: ty::UpvarId,
-                                     upvar_borrow: &mut ty::UpvarBorrow,
-                                     kind: ty::BorrowKind) {
-    debug!("adjust_upvar_borrow_kind_for_loan: upvar_id={} kind={} -> {}",
-           upvar_id, upvar_borrow.kind, kind);
-
-    adjust_upvar_borrow_kind(rcx, upvar_id, upvar_borrow, kind)
-}
-
-/// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
-/// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
-/// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
-/// some particular use.
-fn adjust_upvar_borrow_kind(rcx: &Rcx,
-                            upvar_id: ty::UpvarId,
-                            upvar_borrow: &mut ty::UpvarBorrow,
-                            kind: ty::BorrowKind) {
-    debug!("adjust_upvar_borrow_kind: id={} kind=({} -> {})",
-           upvar_id, upvar_borrow.kind, kind);
-
-    match (upvar_borrow.kind, kind) {
-        // Take RHS:
-        (ty::ImmBorrow, ty::UniqueImmBorrow) |
-        (ty::ImmBorrow, ty::MutBorrow) |
-        (ty::UniqueImmBorrow, ty::MutBorrow) => {
-            upvar_borrow.kind = kind;
-
-            // Check if there are any region links we now need to
-            // establish due to adjusting the borrow kind of the upvar
-            match rcx.maybe_links.borrow_mut().entry(upvar_id) {
-                Occupied(entry) => {
-                    for MaybeLink { span, borrow_region,
-                                    borrow_kind, borrow_cmt } in entry.take().into_iter()
-                    {
-                        link_region(rcx, span, borrow_region, borrow_kind, borrow_cmt);
-                    }
-                }
-                Vacant(_) => {}
-            }
-        }
-        // Take LHS:
-        (ty::ImmBorrow, ty::ImmBorrow) |
-        (ty::UniqueImmBorrow, ty::ImmBorrow) |
-        (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
-        (ty::MutBorrow, _) => {
-        }
-    }
-}
-
 /// Ensures that all borrowed data reachable via `ty` outlives `region`.
 fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
                                origin: infer::SubregionOrigin<'tcx>,
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
new file mode 100644
index 00000000000..a4c3550fcd6
--- /dev/null
+++ b/src/librustc_typeck/check/upvar.rs
@@ -0,0 +1,373 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! ### Inferring borrow kinds for upvars
+//!
+//! Whenever there is a closure expression, we need to determine how each
+//! upvar is used. We do this by initially assigning each upvar an
+//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then
+//! "escalating" the kind as needed. The borrow kind proceeds according to
+//! the following lattice:
+//!
+//!     ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
+//!
+//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
+//! will promote its borrow kind to mutable borrow. If we see an `&mut x`
+//! we'll do the same. Naturally, this applies not just to the upvar, but
+//! to everything owned by `x`, so the result is the same for something
+//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
+//! struct). These adjustments are performed in
+//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
+//! from there).
+//!
+//! The fact that we are inferring borrow kinds as we go results in a
+//! semi-hacky interaction with mem-categorization. In particular,
+//! mem-categorization will query the current borrow kind as it
+//! categorizes, and we'll return the *current* value, but this may get
+//! adjusted later. Therefore, in this module, we generally ignore the
+//! borrow kind (and derived mutabilities) that are returned from
+//! mem-categorization, since they may be inaccurate. (Another option
+//! would be to use a unification scheme, where instead of returning a
+//! concrete borrow kind like `ty::ImmBorrow`, we return a
+//! `ty::InferBorrow(upvar_id)` or something like that, but this would
+//! then mean that all later passes would have to check for these figments
+//! and report an error, and it just seems like more mess in the end.)
+
+use super::FnCtxt;
+
+use middle::expr_use_visitor as euv;
+use middle::mem_categorization as mc;
+use middle::ty::{mod};
+use middle::infer::{InferCtxt, UpvarRegion};
+use syntax::ast;
+use syntax::codemap::Span;
+use syntax::visit::{mod, Visitor};
+use util::ppaux::Repr;
+
+///////////////////////////////////////////////////////////////////////////
+// PUBLIC ENTRY POINTS
+
+pub fn closure_analyze_fn(fcx: &FnCtxt,
+                          _id: ast::NodeId,
+                          decl: &ast::FnDecl,
+                          body: &ast::Block) {
+    let mut seed = SeedBorrowKind::new(fcx);
+    seed.visit_block(body);
+
+    let mut adjust = AdjustBorrowKind::new(fcx);
+    adjust.analyze_fn(decl, body);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// SEED BORROW KIND
+
+struct SeedBorrowKind<'a,'tcx:'a> {
+    fcx: &'a FnCtxt<'a,'tcx>,
+}
+
+impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
+    fn visit_expr(&mut self, expr: &ast::Expr) {
+        match expr.node {
+            ast::ExprClosure(cc, _, _, ref body) => {
+                self.check_closure(expr, cc, &**body);
+            }
+
+            _ => { }
+        }
+
+        visit::walk_expr(self, expr);
+    }
+
+    fn visit_fn(&mut self,
+                fn_kind: visit::FnKind<'v>,
+                decl: &'v ast::FnDecl,
+                block: &'v ast::Block,
+                span: Span,
+                _id: ast::NodeId)
+    {
+        match fn_kind {
+            visit::FkItemFn(..) | visit::FkMethod(..) => {
+                // ignore nested fn items
+            }
+            visit::FkFnBlock => {
+                visit::walk_fn(self, fn_kind, decl, block, span);
+            }
+        }
+    }
+}
+
+impl<'a,'tcx> SeedBorrowKind<'a,'tcx> {
+    fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> {
+        SeedBorrowKind { fcx: fcx }
+    }
+
+    fn tcx(&self) -> &'a ty::ctxt<'tcx> {
+        self.fcx.tcx()
+    }
+
+    fn infcx(&self) -> &'a InferCtxt<'a,'tcx> {
+        self.fcx.infcx()
+    }
+
+    fn check_closure(&mut self,
+                     expr: &ast::Expr,
+                     capture_clause: ast::CaptureClause,
+                     _body: &ast::Block)
+    {
+        let is_old_skool_closure = match self.fcx.expr_ty(expr).sty {
+            ty::ty_closure(..) => true,
+            _ => false,
+        };
+
+        match capture_clause {
+            ast::CaptureByValue if !is_old_skool_closure => {
+            }
+            _ => {
+                ty::with_freevars(self.tcx(), expr.id, |freevars| {
+                    for freevar in freevars.iter() {
+                        let var_node_id = freevar.def.local_node_id();
+                        let upvar_id = ty::UpvarId { var_id: var_node_id,
+                                                     closure_expr_id: expr.id };
+                        debug!("seed upvar_id {}", upvar_id);
+                        let origin = UpvarRegion(upvar_id, expr.span);
+                        let freevar_region = self.infcx().next_region_var(origin);
+                        let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
+                                                             region: freevar_region };
+                        self.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id,
+                                                                          upvar_borrow);
+                    }
+                });
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// ADJUST BORROW KIND
+
+struct AdjustBorrowKind<'a,'tcx:'a> {
+    fcx: &'a FnCtxt<'a,'tcx>
+}
+
+impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{
+    fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> {
+        AdjustBorrowKind { fcx: fcx }
+    }
+
+    fn tcx(&self) -> &'a ty::ctxt<'tcx> {
+        self.fcx.tcx()
+    }
+
+    fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) {
+        /*!
+         * Analysis starting point.
+         */
+
+        self.visit_block(body);
+
+        debug!("analyzing fn body with id {}", body.id);
+
+        let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
+        euv.walk_fn(decl, body);
+    }
+
+    /// Indicates that `cmt` is being directly mutated (e.g., assigned
+    /// to). If cmt contains any by-ref upvars, this implies that
+    /// those upvars must be borrowed using an `&mut` borow.
+    fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) {
+        debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
+               cmt.repr(self.tcx()));
+
+        match cmt.cat.clone() {
+            mc::cat_deref(base, _, mc::Unique) |
+            mc::cat_interior(base, _) |
+            mc::cat_downcast(base, _) => {
+                // Interior or owned data is mutable if base is
+                // mutable, so iterate to the base.
+                self.adjust_upvar_borrow_kind_for_mut(base);
+            }
+
+            mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
+            mc::cat_deref(base, _, mc::Implicit(..)) => {
+                if let mc::NoteUpvarRef(upvar_id) = cmt.note {
+                    // if this is an implicit deref of an
+                    // upvar, then we need to modify the
+                    // borrow_kind of the upvar to make sure it
+                    // is inferred to mutable if necessary
+                    let mut upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow_mut();
+                    let ub = &mut upvar_borrow_map[upvar_id];
+                    self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow);
+                } else {
+                    // assignment to deref of an `&mut`
+                    // borrowed pointer implies that the
+                    // pointer itself must be unique, but not
+                    // necessarily *mutable*
+                    self.adjust_upvar_borrow_kind_for_unique(base);
+                }
+            }
+
+            mc::cat_deref(_, _, mc::UnsafePtr(..)) |
+            mc::cat_static_item |
+            mc::cat_rvalue(_) |
+            mc::cat_local(_) |
+            mc::cat_upvar(..) => {
+                return;
+            }
+        }
+    }
+
+    fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
+        debug!("adjust_upvar_borrow_kind_for_unique(cmt={})",
+               cmt.repr(self.tcx()));
+
+        match cmt.cat.clone() {
+            mc::cat_deref(base, _, mc::Unique) |
+            mc::cat_interior(base, _) |
+            mc::cat_downcast(base, _) => {
+                // Interior or owned data is unique if base is
+                // unique.
+                self.adjust_upvar_borrow_kind_for_unique(base);
+            }
+
+            mc::cat_deref(base, _, mc::BorrowedPtr(..)) |
+            mc::cat_deref(base, _, mc::Implicit(..)) => {
+                if let mc::NoteUpvarRef(upvar_id) = cmt.note {
+                    // if this is an implicit deref of an
+                    // upvar, then we need to modify the
+                    // borrow_kind of the upvar to make sure it
+                    // is inferred to unique if necessary
+                    let mut ub = self.fcx.inh.upvar_borrow_map.borrow_mut();
+                    let ub = &mut ub[upvar_id];
+                    self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow);
+                } else {
+                    // for a borrowed pointer to be unique, its
+                    // base must be unique
+                    self.adjust_upvar_borrow_kind_for_unique(base);
+                }
+            }
+
+            mc::cat_deref(_, _, mc::UnsafePtr(..)) |
+            mc::cat_static_item |
+            mc::cat_rvalue(_) |
+            mc::cat_local(_) |
+            mc::cat_upvar(..) => {
+            }
+        }
+    }
+
+    /// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
+    /// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
+    /// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
+    /// some particular use.
+    fn adjust_upvar_borrow_kind(&self,
+                                upvar_id: ty::UpvarId,
+                                upvar_borrow: &mut ty::UpvarBorrow,
+                                kind: ty::BorrowKind) {
+        debug!("adjust_upvar_borrow_kind: id={} kind=({} -> {})",
+               upvar_id, upvar_borrow.kind, kind);
+
+        match (upvar_borrow.kind, kind) {
+            // Take RHS:
+            (ty::ImmBorrow, ty::UniqueImmBorrow) |
+            (ty::ImmBorrow, ty::MutBorrow) |
+            (ty::UniqueImmBorrow, ty::MutBorrow) => {
+                upvar_borrow.kind = kind;
+            }
+            // Take LHS:
+            (ty::ImmBorrow, ty::ImmBorrow) |
+            (ty::UniqueImmBorrow, ty::ImmBorrow) |
+            (ty::UniqueImmBorrow, ty::UniqueImmBorrow) |
+            (ty::MutBorrow, _) => {
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> {
+    fn visit_fn(&mut self,
+                fn_kind: visit::FnKind<'v>,
+                decl: &'v ast::FnDecl,
+                body: &'v ast::Block,
+                span: Span,
+                _id: ast::NodeId)
+    {
+        match fn_kind {
+            visit::FkItemFn(..) | visit::FkMethod(..) => {
+                // ignore nested fn items
+            }
+            visit::FkFnBlock => {
+                self.analyze_fn(decl, body);
+                visit::walk_fn(self, fn_kind, decl, body, span);
+            }
+        }
+    }
+}
+
+impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> {
+    fn consume(&mut self,
+               _consume_id: ast::NodeId,
+               _consume_span: Span,
+               _cmt: mc::cmt<'tcx>,
+               _mode: euv::ConsumeMode)
+    {}
+
+    fn matched_pat(&mut self,
+                   _matched_pat: &ast::Pat,
+                   _cmt: mc::cmt<'tcx>,
+                   _mode: euv::MatchMode)
+    {}
+
+    fn consume_pat(&mut self,
+                   _consume_pat: &ast::Pat,
+                   _cmt: mc::cmt<'tcx>,
+                   _mode: euv::ConsumeMode)
+    {}
+
+    fn borrow(&mut self,
+              borrow_id: ast::NodeId,
+              _borrow_span: Span,
+              cmt: mc::cmt<'tcx>,
+              _loan_region: ty::Region,
+              bk: ty::BorrowKind,
+              _loan_cause: euv::LoanCause)
+    {
+        debug!("borrow(borrow_id={}, cmt={}, bk={})",
+               borrow_id, cmt.repr(self.tcx()), bk);
+
+        match bk {
+            ty::ImmBorrow => { }
+            ty::UniqueImmBorrow => {
+                self.adjust_upvar_borrow_kind_for_unique(cmt);
+            }
+            ty::MutBorrow => {
+                self.adjust_upvar_borrow_kind_for_mut(cmt);
+            }
+        }
+    }
+
+    fn decl_without_init(&mut self,
+                         _id: ast::NodeId,
+                         _span: Span)
+    {}
+
+    fn mutate(&mut self,
+              _assignment_id: ast::NodeId,
+              _assignment_span: Span,
+              assignee_cmt: mc::cmt<'tcx>,
+              _mode: euv::MutateMode)
+    {
+        debug!("mutate(assignee_cmt={})",
+               assignee_cmt.repr(self.tcx()));
+
+        self.adjust_upvar_borrow_kind_for_mut(assignee_cmt);
+    }
+}
+
+
diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs
index 1ef6c114032..8566d1f1e12 100644
--- a/src/librustc_typeck/check/vtable.rs
+++ b/src/librustc_typeck/check/vtable.rs
@@ -287,9 +287,7 @@ pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
     fcx.default_type_parameters();
 
     let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
-    let r = fulfillment_cx.select_all_or_error(fcx.infcx(),
-                                               &fcx.inh.param_env,
-                                               fcx);
+    let r = fulfillment_cx.select_all_or_error(fcx.infcx(), fcx);
     match r {
         Ok(()) => { }
         Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
@@ -302,7 +300,7 @@ pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt)
     match
         fcx.inh.fulfillment_cx
         .borrow_mut()
-        .select_where_possible(fcx.infcx(), &fcx.inh.param_env, fcx)
+        .select_where_possible(fcx.infcx(), fcx)
     {
         Ok(()) => { }
         Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
@@ -316,7 +314,7 @@ pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
     match
         fcx.inh.fulfillment_cx
         .borrow_mut()
-        .select_new_obligations(fcx.infcx(), &fcx.inh.param_env, fcx)
+        .select_new_obligations(fcx.infcx(), fcx)
     {
         Ok(()) => { }
         Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 79e1efa618f..bb308198330 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -482,7 +482,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
             debug!("check_implementations_of_copy: self_type={} (free)",
                    self_type.repr(tcx));
 
-            match ty::can_type_implement_copy(tcx, self_type, &param_env) {
+            match ty::can_type_implement_copy(&param_env, span, self_type) {
                 Ok(()) => {}
                 Err(ty::FieldDoesNotImplementCopy(name)) => {
                     tcx.sess
diff --git a/src/test/compile-fail/borrowck-closures-mut-of-imm.rs b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs
index 6360a913500..8163df5e967 100644
--- a/src/test/compile-fail/borrowck-closures-mut-of-imm.rs
+++ b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs
@@ -24,6 +24,7 @@ fn a(x: &int) {
     //~^ ERROR cannot borrow
     let c2 = || set(&mut *x);
     //~^ ERROR cannot borrow
+    //~| ERROR closure requires unique access
 }
 
 fn main() {
diff --git a/src/test/compile-fail/issue-17551.rs b/src/test/compile-fail/issue-17551.rs
index e7f61a4f3ff..3889b6f4f7d 100644
--- a/src/test/compile-fail/issue-17551.rs
+++ b/src/test/compile-fail/issue-17551.rs
@@ -13,6 +13,6 @@
 struct B<T>;
 
 fn main() {
-    let foo = B; //~ ERROR unable to infer enough type information
-    let closure = |:| foo;
+    let foo = B;
+    let closure = |:| foo; //~ ERROR unable to infer enough type information
 }
diff --git a/src/test/compile-fail/privacy1.rs b/src/test/compile-fail/privacy1.rs
index 50261839b16..41621a934d1 100644
--- a/src/test/compile-fail/privacy1.rs
+++ b/src/test/compile-fail/privacy1.rs
@@ -14,6 +14,9 @@
 #[lang="sized"]
 pub trait Sized {}
 
+#[lang="copy"]
+pub trait Copy {}
+
 mod bar {
     // shouldn't bring in too much
     pub use self::glob::*;
diff --git a/src/test/compile-fail/privacy4.rs b/src/test/compile-fail/privacy4.rs
index b65ad2a2e6a..70e7e2df98a 100644
--- a/src/test/compile-fail/privacy4.rs
+++ b/src/test/compile-fail/privacy4.rs
@@ -12,6 +12,7 @@
 #![no_std] // makes debugging this test *a lot* easier (during resolve)
 
 #[lang = "sized"] pub trait Sized for Sized? {}
+#[lang="copy"] pub trait Copy {}
 
 // Test to make sure that private items imported through globs remain private
 // when  they're used.