From 6c4b961effdbec1bb2fc5aaed5aeb219eb7f3b19 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 17 May 2017 08:01:04 -0400
Subject: [PATCH 1/8] move projection mode into parameter environment

---
 src/librustc/infer/mod.rs                     | 58 +++++++------------
 src/librustc/middle/intrinsicck.rs            |  3 +-
 src/librustc/traits/mod.rs                    |  9 +--
 src/librustc/traits/project.rs                | 16 ++---
 src/librustc/traits/select.rs                 |  2 +-
 src/librustc/traits/specialize/mod.rs         |  4 +-
 .../traits/specialize/specialization_graph.rs |  2 +-
 src/librustc/traits/trans/mod.rs              |  2 +-
 src/librustc/ty/maps.rs                       |  6 ++
 src/librustc/ty/mod.rs                        | 14 +++--
 src/librustc/ty/util.rs                       | 28 ++++++---
 src/librustc_const_eval/check_match.rs        |  3 +-
 src/librustc_const_eval/eval.rs               |  2 +-
 src/librustc_lint/builtin.rs                  |  4 +-
 src/librustc_lint/types.rs                    |  2 +-
 src/librustc_mir/build/mod.rs                 |  5 +-
 src/librustc_mir/transform/inline.rs          |  6 +-
 src/librustc_mir/transform/qualify_consts.rs  |  2 +-
 src/librustc_mir/transform/type_check.rs      |  4 +-
 src/librustc_passes/consts.rs                 |  4 +-
 src/librustc_trans/context.rs                 |  8 +--
 src/librustc_trans/glue.rs                    |  2 +-
 src/librustc_typeck/check/compare_method.rs   |  7 ++-
 src/librustc_typeck/check/dropck.rs           |  4 +-
 src/librustc_typeck/check/mod.rs              |  4 +-
 src/librustc_typeck/coherence/builtin.rs      |  4 +-
 .../coherence/inherent_impls_overlap.rs       |  2 +-
 src/librustc_typeck/lib.rs                    |  2 +-
 28 files changed, 110 insertions(+), 99 deletions(-)

diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index f05f411945089..5dbf30d8fa8a4 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -174,11 +174,6 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     // avoid reporting the same error twice.
     pub reported_trait_errors: RefCell<FxHashSet<traits::TraitErrorKey<'tcx>>>,
 
-    // Sadly, the behavior of projection varies a bit depending on the
-    // stage of compilation. The specifics are given in the
-    // documentation for `Reveal`.
-    projection_mode: Reveal,
-
     // When an error occurs, we want to avoid reporting "derived"
     // errors that are due to this original failure. Normally, we
     // handle this with the `err_count_on_creation` count, which
@@ -406,15 +401,15 @@ pub trait InferEnv<'a, 'tcx> {
     fn to_parts(self, tcx: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
                     Option<ty::TypeckTables<'tcx>>,
-                    Option<ty::ParamEnv<'tcx>>);
+                    ty::ParamEnv<'tcx>);
 }
 
-impl<'a, 'tcx> InferEnv<'a, 'tcx> for () {
+impl<'a, 'tcx> InferEnv<'a, 'tcx> for Reveal {
     fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
                     Option<ty::TypeckTables<'tcx>>,
-                    Option<ty::ParamEnv<'tcx>>) {
-        (None, None, None)
+                    ty::ParamEnv<'tcx>) {
+        (None, None, ty::ParamEnv::empty(self))
     }
 }
 
@@ -422,8 +417,8 @@ impl<'a, 'tcx> InferEnv<'a, 'tcx> for ty::ParamEnv<'tcx> {
     fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
                     Option<ty::TypeckTables<'tcx>>,
-                    Option<ty::ParamEnv<'tcx>>) {
-        (None, None, Some(self))
+                    ty::ParamEnv<'tcx>) {
+        (None, None, self)
     }
 }
 
@@ -431,8 +426,8 @@ impl<'a, 'tcx> InferEnv<'a, 'tcx> for (&'a ty::TypeckTables<'tcx>, ty::ParamEnv<
     fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
                     Option<ty::TypeckTables<'tcx>>,
-                    Option<ty::ParamEnv<'tcx>>) {
-        (Some(self.0), None, Some(self.1))
+                    ty::ParamEnv<'tcx>) {
+        (Some(self.0), None, self.1)
     }
 }
 
@@ -440,8 +435,8 @@ impl<'a, 'tcx> InferEnv<'a, 'tcx> for (ty::TypeckTables<'tcx>, ty::ParamEnv<'tcx
     fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
                     Option<ty::TypeckTables<'tcx>>,
-                    Option<ty::ParamEnv<'tcx>>) {
-        (None, Some(self.0), Some(self.1))
+                    ty::ParamEnv<'tcx>) {
+        (None, Some(self.0), self.1)
     }
 }
 
@@ -449,11 +444,11 @@ impl<'a, 'tcx> InferEnv<'a, 'tcx> for hir::BodyId {
     fn to_parts(self, tcx: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
                     Option<ty::TypeckTables<'tcx>>,
-                    Option<ty::ParamEnv<'tcx>>) {
+                    ty::ParamEnv<'tcx>) {
         let def_id = tcx.hir.body_owner_def_id(self);
         (Some(tcx.typeck_tables_of(def_id)),
          None,
-         Some(tcx.param_env(def_id)))
+         tcx.param_env(def_id))
     }
 }
 
@@ -465,15 +460,11 @@ pub struct InferCtxtBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     arena: DroplessArena,
     fresh_tables: Option<RefCell<ty::TypeckTables<'tcx>>>,
     tables: Option<&'a ty::TypeckTables<'gcx>>,
-    param_env: Option<ty::ParamEnv<'gcx>>,
-    projection_mode: Reveal,
+    param_env: ty::ParamEnv<'gcx>,
 }
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
-    pub fn infer_ctxt<E: InferEnv<'a, 'gcx>>(self,
-                                             env: E,
-                                             projection_mode: Reveal)
-                                             -> InferCtxtBuilder<'a, 'gcx, 'tcx> {
+    pub fn infer_ctxt<E: InferEnv<'a, 'gcx>>(self, env: E) -> InferCtxtBuilder<'a, 'gcx, 'tcx> {
         let (tables, fresh_tables, param_env) = env.to_parts(self);
         InferCtxtBuilder {
             global_tcx: self,
@@ -481,7 +472,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
             fresh_tables: fresh_tables.map(RefCell::new),
             tables: tables,
             param_env: param_env,
-            projection_mode: projection_mode,
         }
     }
 
@@ -498,12 +488,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
             int_unification_table: RefCell::new(UnificationTable::new()),
             float_unification_table: RefCell::new(UnificationTable::new()),
             region_vars: RegionVarBindings::new(self),
-            param_env: param_env.unwrap(),
+            param_env: param_env,
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
             projection_cache: RefCell::new(traits::ProjectionCache::new()),
             reported_trait_errors: RefCell::new(FxHashSet()),
-            projection_mode: Reveal::UserFacing,
             tainted_by_errors_flag: Cell::new(false),
             err_count_on_creation: self.sess.err_count(),
             in_snapshot: Cell::new(false),
@@ -520,13 +509,11 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
             ref arena,
             ref fresh_tables,
             tables,
-            ref mut param_env,
-            projection_mode,
+            param_env,
         } = *self;
         let tables = tables.map(InferTables::Interned).unwrap_or_else(|| {
             fresh_tables.as_ref().map_or(InferTables::Missing, InferTables::InProgress)
         });
-        let param_env = param_env.take().unwrap_or_else(|| ty::ParamEnv::empty());
         global_tcx.enter_local(arena, |tcx| f(InferCtxt {
             tcx: tcx,
             tables: tables,
@@ -539,7 +526,6 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
             reported_trait_errors: RefCell::new(FxHashSet()),
-            projection_mode: projection_mode,
             tainted_by_errors_flag: Cell::new(false),
             err_count_on_creation: tcx.sess.err_count(),
             in_snapshot: Cell::new(false),
@@ -643,11 +629,15 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
             return value;
         }
 
-        self.infer_ctxt((), Reveal::All).enter(|infcx| {
+        self.infer_ctxt(Reveal::All).enter(|infcx| {
             value.trans_normalize(&infcx)
         })
     }
 
+    /// Does a best-effort to normalize any associated types in
+    /// `value`; this includes revealing specializable types, so this
+    /// should be not be used during type-checking, but only during
+    /// optimization and code generation.
     pub fn normalize_associated_type_in_env<T>(
         self, value: &T, env: ty::ParamEnv<'tcx>
     ) -> T
@@ -661,7 +651,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
             return value;
         }
 
-        self.infer_ctxt(env, Reveal::All).enter(|infcx| {
+        self.infer_ctxt(env.reveal_all()).enter(|infcx| {
             value.trans_normalize(&infcx)
        })
     }
@@ -728,10 +718,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    pub fn projection_mode(&self) -> Reveal {
-        self.projection_mode
-    }
-
     pub fn is_in_snapshot(&self) -> bool {
         self.in_snapshot.get()
     }
diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs
index a759a9061f842..08f731ae34def 100644
--- a/src/librustc/middle/intrinsicck.rs
+++ b/src/librustc/middle/intrinsicck.rs
@@ -11,7 +11,6 @@
 use hir::def::Def;
 use hir::def_id::DefId;
 use infer::InferCtxt;
-use traits::Reveal;
 use ty::{self, Ty, TyCtxt};
 use ty::layout::{LayoutError, Pointer, SizeSkeleton};
 
@@ -140,7 +139,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ItemVisitor<'a, 'tcx> {
 
     fn visit_nested_body(&mut self, body_id: hir::BodyId) {
         let body = self.tcx.hir.body(body_id);
-        self.tcx.infer_ctxt(body_id, Reveal::All).enter(|infcx| {
+        self.tcx.infer_ctxt(body_id).enter(|infcx| {
             let mut visitor = ExprVisitor {
                 infcx: &infcx
             };
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index e358f39bd9a3a..fee6ce95a3f7f 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -477,9 +477,10 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     debug!("normalize_param_env_or_error: elaborated-predicates={:?}",
            predicates);
 
-    let elaborated_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates));
+    let elaborated_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
+                                           unnormalized_env.reveal);
 
-    tcx.infer_ctxt(elaborated_env, Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(elaborated_env).enter(|infcx| {
         let predicates = match fully_normalize(
                 &infcx, cause,
                 // You would really want to pass infcx.param_env.caller_bounds here,
@@ -528,7 +529,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         debug!("normalize_param_env_or_error: resolved predicates={:?}",
             predicates);
 
-        ty::ParamEnv::new(tcx.intern_predicates(&predicates))
+        ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal)
     })
 }
 
@@ -590,7 +591,7 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     debug!("normalize_and_test_predicates(predicates={:?})",
            predicates);
 
-    tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
+    tcx.infer_ctxt(Reveal::All).enter(|infcx| {
         let mut selcx = SelectionContext::new(&infcx);
         let mut fulfill_cx = FulfillmentContext::new();
         let cause = ObligationCause::dummy();
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index f5672ffbdc534..467783fcd7d86 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -36,7 +36,7 @@ use util::common::FN_OUTPUT_NAME;
 
 /// Depending on the stage of compilation, we want projection to be
 /// more or less conservative.
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum Reveal {
     /// At type-checking time, we refuse to project any associated
     /// type that is marked `default`. Non-`default` ("final") types
@@ -278,12 +278,14 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
         match ty.sty {
             ty::TyAnon(def_id, substs) if !substs.has_escaping_regions() => { // (*)
                 // Only normalize `impl Trait` after type-checking, usually in trans.
-                if self.selcx.projection_mode() == Reveal::All {
-                    let generic_ty = self.tcx().type_of(def_id);
-                    let concrete_ty = generic_ty.subst(self.tcx(), substs);
-                    self.fold_ty(concrete_ty)
-                } else {
-                    ty
+                match self.param_env.reveal {
+                    Reveal::UserFacing => ty,
+
+                    Reveal::All => {
+                        let generic_ty = self.tcx().type_of(def_id);
+                        let concrete_ty = generic_ty.subst(self.tcx(), substs);
+                        self.fold_ty(concrete_ty)
+                    }
                 }
             }
 
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 7366ed45f31bd..12f9e2f355bb9 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -324,7 +324,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     pub fn projection_mode(&self) -> Reveal {
-        self.infcx.projection_mode()
+        self.param_env().reveal
     }
 
     /// Wraps the inference context's in_snapshot s.t. snapshot handling is only from the selection
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index e0f28e3b49e91..4d7fdbd881ec3 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -122,7 +122,7 @@ pub fn find_associated_item<'a, 'tcx>(
     let ancestors = trait_def.ancestors(tcx, impl_data.impl_def_id);
     match ancestors.defs(tcx, item.name, item.kind).next() {
         Some(node_item) => {
-            let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
+            let substs = tcx.infer_ctxt(Reveal::All).enter(|infcx| {
                 let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
                 let substs = translate_substs(&infcx, impl_data.impl_def_id,
                                               substs, node_item.node);
@@ -184,7 +184,7 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
 
     // Create a infcx, taking the predicates of impl1 as assumptions:
-    let result = tcx.infer_ctxt(penv, Reveal::UserFacing).enter(|infcx| {
+    let result = tcx.infer_ctxt(penv).enter(|infcx| {
         // Normalize the trait reference. The WF rules ought to ensure
         // that this always succeeds.
         let impl1_trait_ref =
diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs
index 87c98a0ef0ed6..aa35dfd1d70ad 100644
--- a/src/librustc/traits/specialize/specialization_graph.rs
+++ b/src/librustc/traits/specialize/specialization_graph.rs
@@ -109,7 +109,7 @@ impl<'a, 'gcx, 'tcx> Children {
             let possible_sibling = *slot;
 
             let tcx = tcx.global_tcx();
-            let (le, ge) = tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
+            let (le, ge) = tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
                 let overlap = traits::overlapping_impls(&infcx,
                                                         possible_sibling,
                                                         impl_def_id);
diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs
index e38306aed2a91..4cffe6af083b6 100644
--- a/src/librustc/traits/trans/mod.rs
+++ b/src/librustc/traits/trans/mod.rs
@@ -46,7 +46,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
 
             // Do the initial selection for the obligation. This yields the
             // shallow result we are looking for -- that is, what specific impl.
-            self.infer_ctxt((), Reveal::All).enter(|infcx| {
+            self.infer_ctxt(Reveal::All).enter(|infcx| {
                 let mut selcx = SelectionContext::new(&infcx);
 
                 let obligation_cause = ObligationCause::misc(span,
diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs
index cfb9e648d3b7e..da85d40b2c3e8 100644
--- a/src/librustc/ty/maps.rs
+++ b/src/librustc/ty/maps.rs
@@ -906,6 +906,12 @@ define_maps! { <'tcx>
     [] specialization_graph_of: SpecializationGraph(DefId) -> Rc<specialization_graph::Graph>,
     [] is_object_safe: ObjectSafety(DefId) -> bool,
 
+    // Get the ParameterEnvironment for a given item; this environment
+    // will be in "user-facing" mode, meaning that it is suitabe for
+    // type-checking etc, and it does not normalize specializable
+    // associated types. This is almost always what you want,
+    // unless you are doing MIR optimizations, in which case you
+    // might want to use `reveal_all()` method to change modes.
     [] param_env: ParamEnv(DefId) -> ty::ParamEnv<'tcx>,
 
     // Trait selection queries. These are best used by invoking `ty.moves_by_default()`,
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index b495b5ee81a9a..0a25cd638cc30 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1191,6 +1191,11 @@ pub struct ParamEnv<'tcx> {
     /// the set of bounds on the in-scope type parameters, translated
     /// into Obligations, and elaborated and normalized.
     pub caller_bounds: &'tcx Slice<ty::Predicate<'tcx>>,
+
+    /// Typically, this is `Reveal::UserFacing`, but during trans we
+    /// want `Reveal::All` -- note that this is always paired with an
+    /// empty environment. To get that, use `ParamEnv::reveal()`.
+    pub reveal: traits::Reveal,
 }
 
 impl<'tcx> ParamEnv<'tcx> {
@@ -1218,7 +1223,7 @@ impl<'tcx> ParamEnv<'tcx> {
             }
         } else {
             ParamEnvAnd {
-                param_env: ParamEnv::empty(),
+                param_env: ParamEnv::empty(self.reveal),
                 value: value,
             }
         }
@@ -2467,8 +2472,8 @@ fn trait_of_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Option
 
 /// See `ParamEnv` struct def'n for details.
 fn param_env<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                   def_id: DefId)
-                                   -> ParamEnv<'tcx> {
+                       def_id: DefId)
+                       -> ParamEnv<'tcx> {
     // Compute the bounds on Self and the type parameters.
 
     let bounds = tcx.predicates_of(def_id).instantiate_identity(tcx);
@@ -2486,7 +2491,8 @@ fn param_env<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // are any errors at that point, so after type checking you can be
     // sure that this will succeed without errors anyway.
 
-    let unnormalized_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates));
+    let unnormalized_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
+                                             traits::Reveal::UserFacing);
 
     let body_id = tcx.hir.as_local_node_id(def_id).map_or(DUMMY_NODE_ID, |id| {
         tcx.hir.maybe_body_owned_by(id).map_or(id, |body| body.node_id)
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index ce0f1ed5bb86c..31c099c661df0 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -150,20 +150,32 @@ pub enum Representability {
 impl<'tcx> ty::ParamEnv<'tcx> {
     /// Construct a trait environment suitable for contexts where
     /// there are no where clauses in scope.
-    pub fn empty() -> Self {
-        Self::new(ty::Slice::empty())
+    pub fn empty(reveal: Reveal) -> Self {
+        Self::new(ty::Slice::empty(), reveal)
     }
 
     /// Construct a trait environment with the given set of predicates.
-    pub fn new(caller_bounds: &'tcx ty::Slice<ty::Predicate<'tcx>>) -> Self {
-        ty::ParamEnv { caller_bounds }
+    pub fn new(caller_bounds: &'tcx ty::Slice<ty::Predicate<'tcx>>,
+               reveal: Reveal)
+               -> Self {
+        ty::ParamEnv { caller_bounds, reveal }
+    }
+
+    /// Returns a new parameter environment with the same clauses, but
+    /// which "reveals" the true results of projections in all cases
+    /// (even for associated types that are specializable).  This is
+    /// the desired behavior during trans and certain other special
+    /// contexts; normally though we want to use `Reveal::UserFacing`,
+    /// which is the default.
+    pub fn reveal_all(self) -> Self {
+        ty::ParamEnv { reveal: Reveal::All, ..self }
     }
 
     pub fn can_type_implement_copy<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                        self_type: Ty<'tcx>, span: Span)
                                        -> Result<(), CopyImplementationError> {
         // FIXME: (@jroesch) float this code up
-        tcx.infer_ctxt(self.clone(), Reveal::UserFacing).enter(|infcx| {
+        tcx.infer_ctxt(self.clone()).enter(|infcx| {
             let (adt, substs) = match self_type.sty {
                 ty::TyAdt(adt, substs) => (adt, substs),
                 _ => return Err(CopyImplementationError::NotAnAdt),
@@ -970,7 +982,7 @@ fn is_copy_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 {
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::CopyTraitLangItem);
-    tcx.infer_ctxt(param_env, Reveal::UserFacing)
+    tcx.infer_ctxt(param_env)
        .enter(|infcx| traits::type_known_to_meet_bound(&infcx, ty, trait_def_id, DUMMY_SP))
 }
 
@@ -980,7 +992,7 @@ fn is_sized_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 {
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::SizedTraitLangItem);
-    tcx.infer_ctxt(param_env, Reveal::UserFacing)
+    tcx.infer_ctxt(param_env)
        .enter(|infcx| traits::type_known_to_meet_bound(&infcx, ty, trait_def_id, DUMMY_SP))
 }
 
@@ -990,7 +1002,7 @@ fn is_freeze_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 {
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::FreezeTraitLangItem);
-    tcx.infer_ctxt(param_env, Reveal::UserFacing)
+    tcx.infer_ctxt(param_env)
        .enter(|infcx| traits::type_known_to_meet_bound(&infcx, ty, trait_def_id, DUMMY_SP))
 }
 
diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs
index b35b086599165..39db384e2ded2 100644
--- a/src/librustc_const_eval/check_match.rs
+++ b/src/librustc_const_eval/check_match.rs
@@ -20,7 +20,6 @@ use rustc::middle::expr_use_visitor as euv;
 use rustc::middle::mem_categorization::{cmt};
 use rustc::middle::region::RegionMaps;
 use rustc::session::Session;
-use rustc::traits::Reveal;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::lint;
 use rustc_errors::{Diagnostic, Level, DiagnosticBuilder};
@@ -518,7 +517,7 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
 ///
 /// FIXME: this should be done by borrowck.
 fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Expr) {
-    cx.tcx.infer_ctxt((cx.tables, cx.param_env), Reveal::UserFacing).enter(|infcx| {
+    cx.tcx.infer_ctxt((cx.tables, cx.param_env)).enter(|infcx| {
         let mut checker = MutationChecker {
             cx: cx,
         };
diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs
index a6b39f22277de..1364898b549d4 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -483,7 +483,7 @@ fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     debug!("resolve_trait_associated_const: trait_ref={:?}",
            trait_ref);
 
-    tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
         let mut selcx = traits::SelectionContext::new(&infcx);
         let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
                                                  trait_ref.to_poly_trait_predicate());
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 12bfb1e02cf6f..619e7ec6a4f60 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -488,7 +488,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations {
         if def.has_dtor(cx.tcx) {
             return;
         }
-        let param_env = ty::ParamEnv::empty();
+        let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
         if !ty.moves_by_default(cx.tcx, param_env, item.span) {
             return;
         }
@@ -956,7 +956,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
                                                 trait_ref.to_poly_trait_predicate());
 
                     let param_env = tcx.param_env(method.def_id);
-                    tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
+                    tcx.infer_ctxt(param_env).enter(|infcx| {
                         let mut selcx = traits::SelectionContext::new(&infcx);
                         match selcx.select(&obligation) {
                             // The method comes from a `T: Trait` bound.
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index c2181c9764c6c..1b86085d99d1b 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -725,7 +725,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
             if gens.ty_params.is_empty() {
                 // sizes only make sense for non-generic types
                 let t = cx.tcx.type_of(cx.tcx.hir.local_def_id(it.id));
-                let layout = cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
+                let layout = cx.tcx.infer_ctxt(Reveal::All).enter(|infcx| {
                     let ty = cx.tcx.erase_regions(&t);
                     ty.layout(&infcx).unwrap_or_else(|e| {
                         bug!("failed to get layout for `{}`: {}", t, e)
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 407b08d283149..e9cf3115ddab1 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -18,7 +18,6 @@ use rustc::middle::region::CodeExtent;
 use rustc::mir::*;
 use rustc::mir::transform::MirSource;
 use rustc::mir::visit::MutVisitor;
-use rustc::traits::Reveal;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::subst::Substs;
 use rustc::util::nodemap::NodeMap;
@@ -84,7 +83,7 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
     };
 
     let src = MirSource::from_node(tcx, id);
-    tcx.infer_ctxt(body_id, Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(body_id).enter(|infcx| {
         let cx = Cx::new(&infcx, src);
         let mut mir = if cx.tables().tainted_by_errors {
             build::construct_error(cx, body_id)
@@ -173,7 +172,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let span = tcx.hir.span(ctor_id);
     if let hir::VariantData::Tuple(ref fields, ctor_id) = *v {
         let pe = tcx.param_env(tcx.hir.local_def_id(ctor_id));
-        tcx.infer_ctxt(pe, Reveal::UserFacing).enter(|infcx| {
+        tcx.infer_ctxt(pe).enter(|infcx| {
             let (mut mir, src) =
                 shim::build_adt_ctor(&infcx, ctor_id, fields, span);
 
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index edb2f44d18e35..8842d30b65c27 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -18,7 +18,6 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc::mir::*;
 use rustc::mir::transform::{MirPass, MirSource};
 use rustc::mir::visit::*;
-use rustc::traits;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::subst::{Subst,Substs};
 
@@ -545,9 +544,10 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
     }
 }
 
-fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>,
+fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                          param_env: ty::ParamEnv<'tcx>,
                           ty: Ty<'tcx>) -> Option<u64> {
-    tcx.infer_ctxt(param_env, traits::Reveal::All).enter(|infcx| {
+    tcx.infer_ctxt(param_env.reveal_all()).enter(|infcx| {
         ty.layout(&infcx).ok().map(|layout| {
             layout.size(&tcx.data_layout).bytes()
         })
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 3b1c54f68e49b..7c49a11ca1f71 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -998,7 +998,7 @@ impl MirPass for QualifyAndPromoteConstants {
         // Statics must be Sync.
         if mode == Mode::Static {
             let ty = mir.return_ty;
-            tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
+            tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
                 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
                 let mut fulfillment_cx = traits::FulfillmentContext::new();
                 fulfillment_cx.register_bound(&infcx, ty,
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index 8258627748f30..f7055f90f0f83 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -12,7 +12,7 @@
 #![allow(unreachable_code)]
 
 use rustc::infer::{self, InferCtxt, InferOk};
-use rustc::traits::{self, Reveal};
+use rustc::traits;
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::{self, Ty, TyCtxt, TypeVariants};
 use rustc::middle::const_val::ConstVal;
@@ -752,7 +752,7 @@ impl MirPass for TypeckMir {
             return;
         }
         let param_env = tcx.param_env(def_id);
-        tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
+        tcx.infer_ctxt(param_env).enter(|infcx| {
             let mut checker = TypeChecker::new(&infcx, item_id);
             {
                 let mut verifier = TypeVerifier::new(&mut checker, mir);
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index fecde3a636cda..2a4a13932e3fa 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -138,7 +138,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
             self.check_const_eval(&body.value);
         }
 
-        let outer_penv = self.tcx.infer_ctxt(body_id, Reveal::UserFacing).enter(|infcx| {
+        let outer_penv = self.tcx.infer_ctxt(body_id).enter(|infcx| {
             let param_env = infcx.param_env.clone();
             let outer_penv = mem::replace(&mut self.param_env, param_env);
             let region_maps = &self.tcx.region_maps(item_def_id);
@@ -468,7 +468,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
         in_fn: false,
         promotable: false,
         mut_rvalue_borrows: NodeSet(),
-        param_env: ty::ParamEnv::empty(),
+        param_env: ty::ParamEnv::empty(Reveal::UserFacing),
     }.as_deep_visitor());
     tcx.sess.abort_if_errors();
 }
diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs
index c3b16c2d07d07..56f51cae147ad 100644
--- a/src/librustc_trans/context.rs
+++ b/src/librustc_trans/context.rs
@@ -320,15 +320,15 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
     }
 
     pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool {
-        ty.needs_drop(self.tcx, ty::ParamEnv::empty())
+        ty.needs_drop(self.tcx, ty::ParamEnv::empty(traits::Reveal::All))
     }
 
     pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
-        ty.is_sized(self.tcx, ty::ParamEnv::empty(), DUMMY_SP)
+        ty.is_sized(self.tcx, ty::ParamEnv::empty(traits::Reveal::All), DUMMY_SP)
     }
 
     pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
-        ty.is_freeze(self.tcx, ty::ParamEnv::empty(), DUMMY_SP)
+        ty.is_freeze(self.tcx, ty::ParamEnv::empty(traits::Reveal::All), DUMMY_SP)
     }
 
     pub fn exported_symbols<'a>(&'a self) -> &'a NodeSet {
@@ -735,7 +735,7 @@ impl<'a, 'tcx> LayoutTyper<'tcx> for &'a SharedCrateContext<'a, 'tcx> {
             return TyLayout { ty: ty, layout: layout, variant_index: None };
         }
 
-        self.tcx().infer_ctxt((), traits::Reveal::All).enter(|infcx| {
+        self.tcx().infer_ctxt(traits::Reveal::All).enter(|infcx| {
             infcx.layout_of(ty).unwrap_or_else(|e| {
                 match e {
                     ty::layout::LayoutError::SizeOverflow(_) =>
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index fa400b54d2708..f473d957a9031 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -46,7 +46,7 @@ pub fn needs_drop_glue<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'tcx>
         ty::TyAdt(def, _) if def.is_box() => {
             let typ = t.boxed_ty();
             if !scx.type_needs_drop(typ) && scx.type_is_sized(typ) {
-                scx.tcx().infer_ctxt((), traits::Reveal::All).enter(|infcx| {
+                scx.tcx().infer_ctxt(traits::Reveal::All).enter(|infcx| {
                     let layout = t.layout(&infcx).unwrap();
                     if layout.size(scx).bytes() == 0 {
                         // `Box<ZeroSizeType>` does not allocate.
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index 3121f4948504e..1d6d7fa61001a 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -212,13 +212,14 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // The key step here is to update the caller_bounds's predicates to be
     // the new hybrid bounds we computed.
     let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_node_id);
-    let param_env = ty::ParamEnv::new(tcx.intern_predicates(&hybrid_preds.predicates));
+    let param_env = ty::ParamEnv::new(tcx.intern_predicates(&hybrid_preds.predicates),
+                                      Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(tcx,
                                                          impl_m.def_id,
                                                          param_env,
                                                          normalize_cause.clone());
 
-    tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(param_env).enter(|infcx| {
         let inh = Inherited::new(infcx, impl_m.def_id);
         let infcx = &inh.infcx;
 
@@ -713,7 +714,7 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                     impl_trait_ref: ty::TraitRef<'tcx>) {
     debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
 
-    tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
         let inh = Inherited::new(infcx, impl_c.def_id);
         let infcx = &inh.infcx;
 
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 3ed0da05dc2c2..06f405120ae42 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -16,7 +16,7 @@ use rustc::infer::{self, InferOk};
 use rustc::middle::region::{self, RegionMaps};
 use rustc::ty::subst::{Subst, Substs};
 use rustc::ty::{self, Ty, TyCtxt};
-use rustc::traits::{self, ObligationCause, Reveal};
+use rustc::traits::{self, ObligationCause};
 use util::common::ErrorReported;
 use util::nodemap::FxHashSet;
 
@@ -80,7 +80,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
     // check that the impl type can be made to match the trait type.
 
     let impl_param_env = tcx.param_env(self_type_did);
-    tcx.infer_ctxt(impl_param_env, Reveal::UserFacing).enter(|ref infcx| {
+    tcx.infer_ctxt(impl_param_env).enter(|ref infcx| {
         let tcx = infcx.tcx;
         let mut fulfillment_cx = traits::FulfillmentContext::new();
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 32c3f5c8a5edd..8074c0630e395 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -93,7 +93,7 @@ use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
 use rustc::infer::type_variable::{TypeVariableOrigin};
 use rustc::middle::region::CodeExtent;
 use rustc::ty::subst::{Kind, Subst, Substs};
-use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode, Reveal};
+use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
 use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
 use rustc::ty::{self, Ty, TyCtxt, Visibility};
 use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
@@ -530,7 +530,7 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
         let tables = ty::TypeckTables::empty();
         let param_env = tcx.param_env(def_id);
         InheritedBuilder {
-            infcx: tcx.infer_ctxt((tables, param_env), Reveal::UserFacing),
+            infcx: tcx.infer_ctxt((tables, param_env)),
             def_id,
         }
     }
diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs
index ff5599fb1bdbf..89f2595d1a8fb 100644
--- a/src/librustc_typeck/coherence/builtin.rs
+++ b/src/librustc_typeck/coherence/builtin.rs
@@ -15,7 +15,7 @@ use rustc::middle::free_region::FreeRegionMap;
 use rustc::middle::region::RegionMaps;
 use rustc::middle::lang_items::UnsizeTraitLangItem;
 
-use rustc::traits::{self, ObligationCause, Reveal};
+use rustc::traits::{self, ObligationCause};
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::TypeFoldable;
 use rustc::ty::adjustment::CoerceUnsizedInfo;
@@ -208,7 +208,7 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
            source,
            target);
 
-    tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(param_env).enter(|infcx| {
         let cause = ObligationCause::misc(span, impl_node_id);
         let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
                            mt_b: ty::TypeAndMut<'tcx>,
diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
index 2751e1ff38a50..4aa12d08f61c5 100644
--- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs
+++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
@@ -70,7 +70,7 @@ impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
 
         for (i, &impl1_def_id) in impls.iter().enumerate() {
             for &impl2_def_id in &impls[(i + 1)..] {
-                self.tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
+                self.tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
                     if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() {
                         self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id)
                     }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index baef48fe7d2cd..91dec958a161c 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -155,7 +155,7 @@ fn require_same_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                 expected: Ty<'tcx>,
                                 actual: Ty<'tcx>)
                                 -> bool {
-    tcx.infer_ctxt((), Reveal::UserFacing).enter(|ref infcx| {
+    tcx.infer_ctxt(Reveal::UserFacing).enter(|ref infcx| {
         let mut fulfill_cx = FulfillmentContext::new();
         match infcx.eq_types(false, &cause, expected, actual) {
             Ok(InferOk { obligations, .. }) => {

From 541523d10dce98dc266f6d4e6fb6131182c0f791 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Fri, 19 May 2017 17:27:25 -0400
Subject: [PATCH 2/8] rewrite layout to take a (param-env, ty) pair instead of
 infcx

---
 src/librustc/dep_graph/dep_node.rs   |   2 +
 src/librustc/middle/intrinsicck.rs   |  51 +++---
 src/librustc/ty/layout.rs            | 236 ++++++++++++++-------------
 src/librustc/ty/maps.rs              |  15 ++
 src/librustc/ty/util.rs              |  52 +++---
 src/librustc_lint/types.rs           |  13 +-
 src/librustc_mir/transform/inline.rs |   6 +-
 src/librustc_trans/context.rs        |  46 ++----
 src/librustc_trans/glue.rs           |  16 +-
 9 files changed, 225 insertions(+), 212 deletions(-)

diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 38ad473e4042f..c38b3745f4c72 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -112,6 +112,7 @@ pub enum DepNode<D: Clone + Debug> {
     IsSized(D),
     IsFreeze(D),
     NeedsDrop(D),
+    Layout(D),
 
     // The set of impls for a given trait. Ultimately, it would be
     // nice to get more fine-grained here (e.g., to include a
@@ -241,6 +242,7 @@ impl<D: Clone + Debug> DepNode<D> {
             IsSized(ref d) => op(d).map(IsSized),
             IsFreeze(ref d) => op(d).map(IsFreeze),
             NeedsDrop(ref d) => op(d).map(NeedsDrop),
+            Layout(ref d) => op(d).map(Layout),
             Hir(ref d) => op(d).map(Hir),
             HirBody(ref d) => op(d).map(HirBody),
             MetaData(ref d) => op(d).map(MetaData),
diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs
index 08f731ae34def..57815b7f0b690 100644
--- a/src/librustc/middle/intrinsicck.rs
+++ b/src/librustc/middle/intrinsicck.rs
@@ -10,7 +10,6 @@
 
 use hir::def::Def;
 use hir::def_id::DefId;
-use infer::InferCtxt;
 use ty::{self, Ty, TyCtxt};
 use ty::layout::{LayoutError, Pointer, SizeSkeleton};
 
@@ -30,8 +29,10 @@ struct ItemVisitor<'a, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>
 }
 
-struct ExprVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
-    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>
+struct ExprVisitor<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    tables: &'tcx ty::TypeckTables<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 /// If the type is `Option<T>`, it will return `T`, otherwise
@@ -63,18 +64,18 @@ fn unpack_option_like<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     ty
 }
 
-impl<'a, 'gcx, 'tcx> ExprVisitor<'a, 'gcx, 'tcx> {
+impl<'a, 'tcx> ExprVisitor<'a, 'tcx> {
     fn def_id_is_transmute(&self, def_id: DefId) -> bool {
-        let intrinsic = match self.infcx.tcx.type_of(def_id).sty {
+        let intrinsic = match self.tcx.type_of(def_id).sty {
             ty::TyFnDef(.., bfty) => bfty.abi() == RustIntrinsic,
             _ => return false
         };
-        intrinsic && self.infcx.tcx.item_name(def_id) == "transmute"
+        intrinsic && self.tcx.item_name(def_id) == "transmute"
     }
 
-    fn check_transmute(&self, span: Span, from: Ty<'gcx>, to: Ty<'gcx>) {
-        let sk_from = SizeSkeleton::compute(from, self.infcx);
-        let sk_to = SizeSkeleton::compute(to, self.infcx);
+    fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
+        let sk_from = SizeSkeleton::compute(from, self.tcx, self.param_env);
+        let sk_to = SizeSkeleton::compute(to, self.tcx, self.param_env);
 
         // Check for same size using the skeletons.
         if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
@@ -84,11 +85,11 @@ impl<'a, 'gcx, 'tcx> ExprVisitor<'a, 'gcx, 'tcx> {
 
             // Special-case transmutting from `typeof(function)` and
             // `Option<typeof(function)>` to present a clearer error.
-            let from = unpack_option_like(self.infcx.tcx.global_tcx(), from);
+            let from = unpack_option_like(self.tcx.global_tcx(), from);
             match (&from.sty, sk_to) {
                 (&ty::TyFnDef(..), SizeSkeleton::Known(size_to))
-                        if size_to == Pointer.size(self.infcx) => {
-                    struct_span_err!(self.infcx.tcx.sess, span, E0591,
+                        if size_to == Pointer.size(self.tcx) => {
+                    struct_span_err!(self.tcx.sess, span, E0591,
                                      "`{}` is zero-sized and can't be transmuted to `{}`",
                                      from, to)
                         .span_note(span, "cast with `as` to a pointer instead")
@@ -100,7 +101,7 @@ impl<'a, 'gcx, 'tcx> ExprVisitor<'a, 'gcx, 'tcx> {
         }
 
         // Try to display a sensible error with as much information as possible.
-        let skeleton_string = |ty: Ty<'gcx>, sk| {
+        let skeleton_string = |ty: Ty<'tcx>, sk| {
             match sk {
                 Ok(SizeSkeleton::Known(size)) => {
                     format!("{} bits", size.bits())
@@ -119,7 +120,7 @@ impl<'a, 'gcx, 'tcx> ExprVisitor<'a, 'gcx, 'tcx> {
             }
         };
 
-        struct_span_err!(self.infcx.tcx.sess, span, E0512,
+        struct_span_err!(self.tcx.sess, span, E0512,
                   "transmute called with differently sized types: \
                    {} ({}) to {} ({})",
                   from, skeleton_string(from, sk_from),
@@ -138,32 +139,30 @@ impl<'a, 'tcx> Visitor<'tcx> for ItemVisitor<'a, 'tcx> {
     }
 
     fn visit_nested_body(&mut self, body_id: hir::BodyId) {
+        let owner_def_id = self.tcx.hir.body_owner_def_id(body_id);
         let body = self.tcx.hir.body(body_id);
-        self.tcx.infer_ctxt(body_id).enter(|infcx| {
-            let mut visitor = ExprVisitor {
-                infcx: &infcx
-            };
-            visitor.visit_body(body);
-        });
+        let param_env = self.tcx.param_env(owner_def_id);
+        let tables = self.tcx.typeck_tables_of(owner_def_id);
+        ExprVisitor { tcx: self.tcx, param_env, tables }.visit_body(body);
         self.visit_body(body);
     }
 }
 
-impl<'a, 'gcx, 'tcx> Visitor<'gcx> for ExprVisitor<'a, 'gcx, 'tcx> {
-    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
+impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
+    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
         NestedVisitorMap::None
     }
 
-    fn visit_expr(&mut self, expr: &'gcx hir::Expr) {
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
         let def = if let hir::ExprPath(ref qpath) = expr.node {
-            self.infcx.tables.borrow().qpath_def(qpath, expr.id)
+            self.tables.qpath_def(qpath, expr.id)
         } else {
             Def::Err
         };
         match def {
             Def::Fn(did) if self.def_id_is_transmute(did) => {
-                let typ = self.infcx.tables.borrow().node_id_to_type(expr.id);
-                let typ = self.infcx.tcx.lift_to_global(&typ).unwrap();
+                let typ = self.tables.node_id_to_type(expr.id);
+                let typ = self.tcx.lift_to_global(&typ).unwrap();
                 match typ.sty {
                     ty::TyFnDef(.., sig) if sig.abi() == RustIntrinsic => {
                         let from = sig.inputs().skip_binder()[0];
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 1a8c74ff1f943..4dc764f6d30c2 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -12,9 +12,7 @@ pub use self::Integer::*;
 pub use self::Layout::*;
 pub use self::Primitive::*;
 
-use infer::InferCtxt;
 use session::Session;
-use traits;
 use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
 
 use syntax::ast::{FloatTy, IntTy, UintTy};
@@ -212,6 +210,12 @@ impl<'a> HasDataLayout for &'a TargetDataLayout {
     }
 }
 
+impl<'a, 'tcx> HasDataLayout for TyCtxt<'a, 'tcx, 'tcx> {
+    fn data_layout(&self) -> &TargetDataLayout {
+        &self.data_layout
+    }
+}
+
 /// Endianness of the target, which must match cfg(target-endian).
 #[derive(Copy, Clone)]
 pub enum Endian {
@@ -457,8 +461,12 @@ impl Integer {
     /// signed discriminant range and #[repr] attribute.
     /// N.B.: u64 values above i64::MAX will be treated as signed, but
     /// that shouldn't affect anything, other than maybe debuginfo.
-    fn repr_discr(tcx: TyCtxt, ty: Ty, repr: &ReprOptions, min: i64, max: i64)
-                      -> (Integer, bool) {
+    fn repr_discr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                            ty: Ty<'tcx>,
+                            repr: &ReprOptions,
+                            min: i64,
+                            max: i64)
+                            -> (Integer, bool) {
         // Theoretically, negative values could be larger in unsigned representation
         // than the unsigned representation of the signed minimum. However, if there
         // are any negative values, the only valid unsigned representation is u64
@@ -583,10 +591,13 @@ enum StructKind {
     EnumVariant,
 }
 
-impl<'a, 'gcx, 'tcx> Struct {
-    fn new(dl: &TargetDataLayout, fields: &Vec<&'a Layout>,
-                  repr: &ReprOptions, kind: StructKind,
-                  scapegoat: Ty<'gcx>) -> Result<Struct, LayoutError<'gcx>> {
+impl<'a, 'tcx> Struct {
+    fn new(dl: &TargetDataLayout,
+           fields: &Vec<&'a Layout>,
+           repr: &ReprOptions,
+           kind: StructKind,
+           scapegoat: Ty<'tcx>)
+           -> Result<Struct, LayoutError<'tcx>> {
         if repr.packed() && repr.align > 0 {
             bug!("Struct cannot be packed and aligned");
         }
@@ -723,8 +734,8 @@ impl<'a, 'gcx, 'tcx> Struct {
 
     /// Determine whether a structure would be zero-sized, given its fields.
     fn would_be_zero_sized<I>(dl: &TargetDataLayout, fields: I)
-                              -> Result<bool, LayoutError<'gcx>>
-    where I: Iterator<Item=Result<&'a Layout, LayoutError<'gcx>>> {
+                              -> Result<bool, LayoutError<'tcx>>
+    where I: Iterator<Item=Result<&'a Layout, LayoutError<'tcx>>> {
         for field in fields {
             let field = field?;
             if field.is_unsized() || field.size(dl).bytes() > 0 {
@@ -764,11 +775,11 @@ impl<'a, 'gcx, 'tcx> Struct {
     /// The tuple is `(path, source_path)`,
     /// where `path` is in memory order and `source_path` in source order.
     // FIXME(eddyb) track value ranges and traverse already optimized enums.
-    fn non_zero_field_in_type(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-                               ty: Ty<'gcx>)
-                               -> Result<Option<(FieldPath, FieldPath)>, LayoutError<'gcx>> {
-        let tcx = infcx.tcx.global_tcx();
-        match (ty.layout(infcx)?, &ty.sty) {
+    fn non_zero_field_in_type(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                              param_env: ty::ParamEnv<'tcx>,
+                              ty: Ty<'tcx>)
+                              -> Result<Option<(FieldPath, FieldPath)>, LayoutError<'tcx>> {
+        match (ty.layout(tcx, param_env)?, &ty.sty) {
             (&Scalar { non_zero: true, .. }, _) |
             (&CEnum { non_zero: true, .. }, _) => Ok(Some((vec![], vec![]))),
             (&FatPointer { non_zero: true, .. }, _) => {
@@ -779,7 +790,7 @@ impl<'a, 'gcx, 'tcx> Struct {
             (&Univariant { non_zero: true, .. }, &ty::TyAdt(def, substs)) => {
                 let fields = &def.struct_variant().fields;
                 assert_eq!(fields.len(), 1);
-                match *fields[0].ty(tcx, substs).layout(infcx)? {
+                match *fields[0].ty(tcx, substs).layout(tcx, param_env)? {
                     // FIXME(eddyb) also allow floating-point types here.
                     Scalar { value: Int(_), non_zero: false } |
                     Scalar { value: Pointer, non_zero: false } => {
@@ -796,37 +807,49 @@ impl<'a, 'gcx, 'tcx> Struct {
             // Perhaps one of the fields of this struct is non-zero
             // let's recurse and find out
             (&Univariant { ref variant, .. }, &ty::TyAdt(def, substs)) if def.is_struct() => {
-                Struct::non_zero_field_paths(infcx, def.struct_variant().fields
-                                                      .iter().map(|field| {
-                    field.ty(tcx, substs)
-                }),
-                Some(&variant.memory_index[..]))
+                Struct::non_zero_field_paths(
+                    tcx,
+                    param_env,
+                    def.struct_variant().fields.iter().map(|field| {
+                        field.ty(tcx, substs)
+                    }),
+                    Some(&variant.memory_index[..]))
             }
 
             // Perhaps one of the upvars of this closure is non-zero
             (&Univariant { ref variant, .. }, &ty::TyClosure(def, substs)) => {
                 let upvar_tys = substs.upvar_tys(def, tcx);
-                Struct::non_zero_field_paths(infcx, upvar_tys,
+                Struct::non_zero_field_paths(
+                    tcx,
+                    param_env,
+                    upvar_tys,
                     Some(&variant.memory_index[..]))
             }
             // Can we use one of the fields in this tuple?
             (&Univariant { ref variant, .. }, &ty::TyTuple(tys, _)) => {
-                Struct::non_zero_field_paths(infcx, tys.iter().cloned(),
+                Struct::non_zero_field_paths(
+                    tcx,
+                    param_env,
+                    tys.iter().cloned(),
                     Some(&variant.memory_index[..]))
             }
 
             // Is this a fixed-size array of something non-zero
             // with at least one element?
             (_, &ty::TyArray(ety, d)) if d > 0 => {
-                Struct::non_zero_field_paths(infcx, Some(ety).into_iter(), None)
+                Struct::non_zero_field_paths(
+                    tcx,
+                    param_env,
+                    Some(ety).into_iter(),
+                    None)
             }
 
             (_, &ty::TyProjection(_)) | (_, &ty::TyAnon(..)) => {
-                let normalized = infcx.normalize_projections(ty);
+                let normalized = tcx.normalize_associated_type_in_env(&ty, param_env);
                 if ty == normalized {
                     return Ok(None);
                 }
-                return Struct::non_zero_field_in_type(infcx, normalized);
+                return Struct::non_zero_field_in_type(tcx, param_env, normalized);
             }
 
             // Anything else is not a non-zero type.
@@ -838,13 +861,15 @@ impl<'a, 'gcx, 'tcx> Struct {
     /// the given set of fields and recursing through aggregates.
     /// Returns Some((path, source_path)) on success.
     /// `path` is translated to memory order. `source_path` is not.
-    fn non_zero_field_paths<I>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-                                  fields: I,
-                                  permutation: Option<&[u32]>)
-                                  -> Result<Option<(FieldPath, FieldPath)>, LayoutError<'gcx>>
-    where I: Iterator<Item=Ty<'gcx>> {
+    fn non_zero_field_paths<I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                               param_env: ty::ParamEnv<'tcx>,
+                               fields: I,
+                               permutation: Option<&[u32]>)
+                               -> Result<Option<(FieldPath, FieldPath)>, LayoutError<'tcx>>
+    where I: Iterator<Item=Ty<'tcx>> {
         for (i, ty) in fields.enumerate() {
-            if let Some((mut path, mut source_path)) = Struct::non_zero_field_in_type(infcx, ty)? {
+            let r = Struct::non_zero_field_in_type(tcx, param_env, ty)?;
+            if let Some((mut path, mut source_path)) = r {
                 source_path.push(i as u32);
                 let index = if let Some(p) = permutation {
                     p[i] as usize
@@ -881,7 +906,7 @@ pub struct Union {
     pub packed: bool,
 }
 
-impl<'a, 'gcx, 'tcx> Union {
+impl<'a, 'tcx> Union {
     fn new(dl: &TargetDataLayout, packed: bool) -> Union {
         let align = if packed { dl.i8_align } else { dl.aggregate_align };
         Union {
@@ -895,9 +920,9 @@ impl<'a, 'gcx, 'tcx> Union {
     /// Extend the Struct with more fields.
     fn extend<I>(&mut self, dl: &TargetDataLayout,
                  fields: I,
-                 scapegoat: Ty<'gcx>)
-                 -> Result<(), LayoutError<'gcx>>
-    where I: Iterator<Item=Result<&'a Layout, LayoutError<'gcx>>> {
+                 scapegoat: Ty<'tcx>)
+                 -> Result<(), LayoutError<'tcx>>
+    where I: Iterator<Item=Result<&'a Layout, LayoutError<'tcx>>> {
         for (index, field) in fields.enumerate() {
             let field = field?;
             if field.is_unsized() {
@@ -1067,19 +1092,19 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
     }
 }
 
-impl<'a, 'gcx, 'tcx> Layout {
-    pub fn compute_uncached(ty: Ty<'gcx>,
-                            infcx: &InferCtxt<'a, 'gcx, 'tcx>)
-                            -> Result<&'gcx Layout, LayoutError<'gcx>> {
-        let tcx = infcx.tcx.global_tcx();
+impl<'a, 'tcx> Layout {
+    pub fn compute_uncached(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                            param_env: ty::ParamEnv<'tcx>,
+                            ty: Ty<'tcx>)
+                            -> Result<&'tcx Layout, LayoutError<'tcx>> {
         let success = |layout| Ok(tcx.intern_layout(layout));
         let dl = &tcx.data_layout;
         assert!(!ty.has_infer_types());
 
-        let ptr_layout = |pointee: Ty<'gcx>| {
+        let ptr_layout = |pointee: Ty<'tcx>| {
             let non_zero = !ty.is_unsafe_ptr();
-            let pointee = infcx.normalize_projections(pointee);
-            if pointee.is_sized(tcx, infcx.param_env, DUMMY_SP) {
+            let pointee = tcx.normalize_associated_type_in_env(&pointee, param_env);
+            if pointee.is_sized(tcx, param_env, DUMMY_SP) {
                 Ok(Scalar { value: Pointer, non_zero: non_zero })
             } else {
                 let unsized_part = tcx.struct_tail(pointee);
@@ -1132,7 +1157,7 @@ impl<'a, 'gcx, 'tcx> Layout {
 
             // Arrays and slices.
             ty::TyArray(element, count) => {
-                let element = element.layout(infcx)?;
+                let element = element.layout(tcx, param_env)?;
                 let element_size = element.size(dl);
                 // FIXME(eddyb) Don't use host `usize` for array lengths.
                 let usize_count: usize = count;
@@ -1149,7 +1174,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                 }
             }
             ty::TySlice(element) => {
-                let element = element.layout(infcx)?;
+                let element = element.layout(tcx, param_env)?;
                 Array {
                     sized: false,
                     align: element.align(dl),
@@ -1187,7 +1212,7 @@ impl<'a, 'gcx, 'tcx> Layout {
             ty::TyClosure(def_id, ref substs) => {
                 let tys = substs.upvar_tys(def_id, tcx);
                 let st = Struct::new(dl,
-                    &tys.map(|ty| ty.layout(infcx))
+                    &tys.map(|ty| ty.layout(tcx, param_env))
                       .collect::<Result<Vec<_>, _>>()?,
                     &ReprOptions::default(),
                     StructKind::AlwaysSizedUnivariant, ty)?;
@@ -1198,7 +1223,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                 // FIXME(camlorn): if we ever allow unsized tuples, this needs to be checked.
                 // See the univariant case below to learn how.
                 let st = Struct::new(dl,
-                    &tys.iter().map(|ty| ty.layout(infcx))
+                    &tys.iter().map(|ty| ty.layout(tcx, param_env))
                       .collect::<Result<Vec<_>, _>>()?,
                     &ReprOptions::default(), StructKind::AlwaysSizedUnivariant, ty)?;
                 Univariant { variant: st, non_zero: false }
@@ -1207,7 +1232,7 @@ impl<'a, 'gcx, 'tcx> Layout {
             // SIMD vector types.
             ty::TyAdt(def, ..) if def.repr.simd() => {
                 let element = ty.simd_type(tcx);
-                match *element.layout(infcx)? {
+                match *element.layout(tcx, param_env)? {
                     Scalar { value, .. } => {
                         return success(Vector {
                             element: value,
@@ -1278,7 +1303,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                     };
 
                     let fields = def.variants[0].fields.iter().map(|field| {
-                        field.ty(tcx, substs).layout(infcx)
+                        field.ty(tcx, substs).layout(tcx, param_env)
                     }).collect::<Result<Vec<_>, _>>()?;
                     let layout = if def.is_union() {
                         let mut un = Union::new(dl, def.repr.packed());
@@ -1312,20 +1337,21 @@ impl<'a, 'gcx, 'tcx> Layout {
                     // Nullable pointer optimization
                     for discr in 0..2 {
                         let other_fields = variants[1 - discr].iter().map(|ty| {
-                            ty.layout(infcx)
+                            ty.layout(tcx, param_env)
                         });
                         if !Struct::would_be_zero_sized(dl, other_fields)? {
                             continue;
                         }
-                        let paths = Struct::non_zero_field_paths(infcx,
-                            variants[discr].iter().cloned(),
-                            None)?;
+                        let paths = Struct::non_zero_field_paths(tcx,
+                                                                 param_env,
+                                                                 variants[discr].iter().cloned(),
+                                                                 None)?;
                         let (mut path, mut path_source) = if let Some(p) = paths { p }
                           else { continue };
 
                         // FIXME(eddyb) should take advantage of a newtype.
                         if path == &[0] && variants[discr].len() == 1 {
-                            let value = match *variants[discr][0].layout(infcx)? {
+                            let value = match *variants[discr][0].layout(tcx, param_env)? {
                                 Scalar { value, .. } => value,
                                 CEnum { discr, .. } => Int(discr),
                                 _ => bug!("Layout::compute: `{}`'s non-zero \
@@ -1339,7 +1365,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                         }
 
                         let st = Struct::new(dl,
-                            &variants[discr].iter().map(|ty| ty.layout(infcx))
+                            &variants[discr].iter().map(|ty| ty.layout(tcx, param_env))
                               .collect::<Result<Vec<_>, _>>()?,
                             &def.repr, StructKind::AlwaysSizedUnivariant, ty)?;
 
@@ -1377,7 +1403,7 @@ impl<'a, 'gcx, 'tcx> Layout {
                 let discr = Scalar { value: Int(min_ity), non_zero: false };
                 let mut variants = variants.into_iter().map(|fields| {
                     let mut fields = fields.into_iter().map(|field| {
-                        field.layout(infcx)
+                        field.layout(tcx, param_env)
                     }).collect::<Result<Vec<_>, _>>()?;
                     fields.insert(0, &discr);
                     let st = Struct::new(dl,
@@ -1470,11 +1496,11 @@ impl<'a, 'gcx, 'tcx> Layout {
 
             // Types with no meaningful known layout.
             ty::TyProjection(_) | ty::TyAnon(..) => {
-                let normalized = infcx.normalize_projections(ty);
+                let normalized = tcx.normalize_associated_type_in_env(&ty, param_env);
                 if ty == normalized {
                     return Err(LayoutError::Unknown(ty));
                 }
-                return normalized.layout(infcx);
+                return normalized.layout(tcx, param_env);
             }
             ty::TyParam(_) => {
                 return Err(LayoutError::Unknown(ty));
@@ -1686,21 +1712,22 @@ pub enum SizeSkeleton<'tcx> {
     }
 }
 
-impl<'a, 'gcx, 'tcx> SizeSkeleton<'gcx> {
-    pub fn compute(ty: Ty<'gcx>, infcx: &InferCtxt<'a, 'gcx, 'tcx>)
-                   -> Result<SizeSkeleton<'gcx>, LayoutError<'gcx>> {
-        let tcx = infcx.tcx.global_tcx();
+impl<'a, 'tcx> SizeSkeleton<'tcx> {
+    pub fn compute(ty: Ty<'tcx>,
+                   tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                   param_env: ty::ParamEnv<'tcx>)
+                   -> Result<SizeSkeleton<'tcx>, LayoutError<'tcx>> {
         assert!(!ty.has_infer_types());
 
         // First try computing a static layout.
-        let err = match ty.layout(infcx) {
+        let err = match ty.layout(tcx, param_env) {
             Ok(layout) => {
                 return Ok(SizeSkeleton::Known(layout.size(tcx)));
             }
             Err(err) => err
         };
 
-        let ptr_skeleton = |pointee: Ty<'gcx>| {
+        let ptr_skeleton = |pointee: Ty<'tcx>| {
             let non_zero = !ty.is_unsafe_ptr();
             let tail = tcx.struct_tail(pointee);
             match tail.sty {
@@ -1737,7 +1764,7 @@ impl<'a, 'gcx, 'tcx> SizeSkeleton<'gcx> {
                 // Get a zero-sized variant or a pointer newtype.
                 let zero_or_ptr_variant = |i: usize| {
                     let fields = def.variants[i].fields.iter().map(|field| {
-                        SizeSkeleton::compute(field.ty(tcx, substs), infcx)
+                        SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)
                     });
                     let mut ptr = None;
                     for field in fields {
@@ -1788,11 +1815,11 @@ impl<'a, 'gcx, 'tcx> SizeSkeleton<'gcx> {
             }
 
             ty::TyProjection(_) | ty::TyAnon(..) => {
-                let normalized = infcx.normalize_projections(ty);
+                let normalized = tcx.normalize_associated_type_in_env(&ty, param_env);
                 if ty == normalized {
                     Err(err)
                 } else {
-                    SizeSkeleton::compute(normalized, infcx)
+                    SizeSkeleton::compute(normalized, tcx, param_env)
                 }
             }
 
@@ -1826,71 +1853,53 @@ impl<'tcx> Deref for TyLayout<'tcx> {
     }
 }
 
-pub trait HasTyCtxt<'tcx>: HasDataLayout {
+pub trait LayoutTyper<'tcx>: HasDataLayout {
+    type TyLayout;
+
     fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx>;
+    fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout;
+    fn normalize_projections(self, ty: Ty<'tcx>) -> Ty<'tcx>;
 }
 
-impl<'a, 'gcx, 'tcx> HasDataLayout for TyCtxt<'a, 'gcx, 'tcx> {
-    fn data_layout(&self) -> &TargetDataLayout {
-        &self.data_layout
-    }
+/// Combines a tcx with the parameter environment so that you can
+/// compute layout operations.
+#[derive(Copy, Clone)]
+pub struct LayoutCx<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
-impl<'a, 'gcx, 'tcx> HasTyCtxt<'gcx> for TyCtxt<'a, 'gcx, 'tcx> {
-    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'gcx> {
-        self.global_tcx()
+impl<'a, 'tcx> LayoutCx<'a, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
+        LayoutCx { tcx, param_env }
     }
 }
 
-impl<'a, 'gcx, 'tcx> HasDataLayout for &'a InferCtxt<'a, 'gcx, 'tcx> {
+impl<'a, 'tcx> HasDataLayout for LayoutCx<'a, 'tcx> {
     fn data_layout(&self) -> &TargetDataLayout {
         &self.tcx.data_layout
     }
 }
 
-impl<'a, 'gcx, 'tcx> HasTyCtxt<'gcx> for &'a InferCtxt<'a, 'gcx, 'tcx> {
-    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'gcx> {
-        self.tcx.global_tcx()
-    }
-}
-
-pub trait LayoutTyper<'tcx>: HasTyCtxt<'tcx> {
-    type TyLayout;
-
-    fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout;
-    fn normalize_projections(self, ty: Ty<'tcx>) -> Ty<'tcx>;
-}
+impl<'a, 'tcx> LayoutTyper<'tcx> for LayoutCx<'a, 'tcx> {
+    type TyLayout = Result<TyLayout<'tcx>, LayoutError<'tcx>>;
 
-impl<'a, 'gcx, 'tcx> LayoutTyper<'gcx> for &'a InferCtxt<'a, 'gcx, 'tcx> {
-    type TyLayout = Result<TyLayout<'gcx>, LayoutError<'gcx>>;
+    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
+        self.tcx
+    }
 
-    fn layout_of(self, ty: Ty<'gcx>) -> Self::TyLayout {
+    fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
         let ty = self.normalize_projections(ty);
 
         Ok(TyLayout {
             ty: ty,
-            layout: ty.layout(self)?,
+            layout: ty.layout(self.tcx, self.param_env)?,
             variant_index: None
         })
     }
 
-    fn normalize_projections(self, ty: Ty<'gcx>) -> Ty<'gcx> {
-        if !ty.has_projection_types() {
-            return ty;
-        }
-
-        let mut selcx = traits::SelectionContext::new(self);
-        let cause = traits::ObligationCause::dummy();
-        let traits::Normalized { value: result, obligations } =
-            traits::normalize(&mut selcx, cause, &ty);
-
-        let mut fulfill_cx = traits::FulfillmentContext::new();
-
-        for obligation in obligations {
-            fulfill_cx.register_predicate_obligation(self, obligation);
-        }
-
-        self.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &result)
+    fn normalize_projections(self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        self.tcx.normalize_associated_type_in_env(&ty, self.param_env)
     }
 }
 
@@ -1943,7 +1952,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
         }
     }
 
-    pub fn field_type<C: HasTyCtxt<'tcx>>(&self, cx: C, i: usize) -> Ty<'tcx> {
+    pub fn field_type<C: LayoutTyper<'tcx>>(&self, cx: C, i: usize) -> Ty<'tcx> {
         let tcx = cx.tcx();
 
         let ptr_field_type = |pointee: Ty<'tcx>| {
@@ -2014,7 +2023,10 @@ impl<'a, 'tcx> TyLayout<'tcx> {
         }
     }
 
-    pub fn field<C: LayoutTyper<'tcx>>(&self, cx: C, i: usize) -> C::TyLayout {
+    pub fn field<C: LayoutTyper<'tcx>>(&self,
+                                       cx: C,
+                                       i: usize)
+                                       -> C::TyLayout {
         cx.layout_of(cx.normalize_projections(self.field_type(cx, i)))
     }
 }
diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs
index da85d40b2c3e8..b5adcc8ed757d 100644
--- a/src/librustc/ty/maps.rs
+++ b/src/librustc/ty/maps.rs
@@ -20,6 +20,7 @@ use mir::transform::{MirSuite, MirPassIndex};
 use session::CompileResult;
 use traits::specialization_graph;
 use ty::{self, CrateInherentImpls, Ty, TyCtxt};
+use ty::layout::{Layout, LayoutError};
 use ty::item_path;
 use ty::steal::Steal;
 use ty::subst::Substs;
@@ -293,6 +294,12 @@ impl<'tcx> QueryDescription for queries::needs_drop_raw<'tcx> {
     }
 }
 
+impl<'tcx> QueryDescription for queries::layout_raw<'tcx> {
+    fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
+        format!("computing layout of `{}`", env.value)
+    }
+}
+
 impl<'tcx> QueryDescription for queries::super_predicates_of<'tcx> {
     fn describe(tcx: TyCtxt, def_id: DefId) -> String {
         format!("computing the supertraits of `{}`",
@@ -920,6 +927,8 @@ define_maps! { <'tcx>
     [] is_sized_raw: is_sized_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
     [] is_freeze_raw: is_freeze_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
     [] needs_drop_raw: needs_drop_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
+    [] layout_raw: layout_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
+                                  -> Result<&'tcx Layout, LayoutError<'tcx>>,
 }
 
 fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode<DefId> {
@@ -987,3 +996,9 @@ fn needs_drop_dep_node<'tcx>(key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepNode<De
         .unwrap_or(DefId::local(CRATE_DEF_INDEX));
     DepNode::NeedsDrop(def_id)
 }
+
+fn layout_dep_node<'tcx>(key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepNode<DefId> {
+    let def_id = ty::item_path::characteristic_def_id_of_type(key.value)
+        .unwrap_or(DefId::local(CRATE_DEF_INDEX));
+    DepNode::Layout(def_id)
+}
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 31c099c661df0..6d55f04e86aaa 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -12,7 +12,6 @@
 
 use hir::def_id::{DefId, LOCAL_CRATE};
 use hir::map::DefPathData;
-use infer::InferCtxt;
 use ich::{StableHashingContext, NodeIdHashingMode};
 use traits::{self, Reveal};
 use ty::{self, Ty, TyCtxt, TypeFoldable};
@@ -791,35 +790,17 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
         tcx.needs_drop_raw(param_env.and(self))
     }
 
+    /// Computes the layout of a type. Note that this implicitly
+    /// executes in "reveal all" mode.
     #[inline]
-    pub fn layout<'lcx>(&'tcx self, infcx: &InferCtxt<'a, 'tcx, 'lcx>)
+    pub fn layout<'lcx>(&'tcx self,
+                        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                        param_env: ty::ParamEnv<'tcx>)
                         -> Result<&'tcx Layout, LayoutError<'tcx>> {
-        let tcx = infcx.tcx.global_tcx();
-        let can_cache = !self.has_param_types() && !self.has_self_ty();
-        if can_cache {
-            if let Some(&cached) = tcx.layout_cache.borrow().get(&self) {
-                return Ok(cached);
-            }
-        }
-
-        let rec_limit = tcx.sess.recursion_limit.get();
-        let depth = tcx.layout_depth.get();
-        if depth > rec_limit {
-            tcx.sess.fatal(
-                &format!("overflow representing the type `{}`", self));
-        }
-
-        tcx.layout_depth.set(depth+1);
-        let layout = Layout::compute_uncached(self, infcx);
-        tcx.layout_depth.set(depth);
-        let layout = layout?;
-        if can_cache {
-            tcx.layout_cache.borrow_mut().insert(self, layout);
-        }
-        Ok(layout)
+        let ty = tcx.erase_regions(&self);
+        tcx.layout_raw(param_env.reveal_all().and(ty))
     }
 
-
     /// Check whether a type is representable. This means it cannot contain unboxed
     /// structural recursion. This check is needed for structs and enums.
     pub fn is_representable(&'tcx self,
@@ -1074,6 +1055,24 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
 }
 
+fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                        query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
+                        -> Result<&'tcx Layout, LayoutError<'tcx>>
+{
+    let (param_env, ty) = query.into_parts();
+
+    let rec_limit = tcx.sess.recursion_limit.get();
+    let depth = tcx.layout_depth.get();
+    if depth > rec_limit {
+        tcx.sess.fatal(
+            &format!("overflow representing the type `{}`", ty));
+    }
+
+    tcx.layout_depth.set(depth+1);
+    let layout = Layout::compute_uncached(tcx, param_env, ty);
+    tcx.layout_depth.set(depth);
+    layout
+}
 
 pub fn provide(providers: &mut ty::maps::Providers) {
     *providers = ty::maps::Providers {
@@ -1081,6 +1080,7 @@ pub fn provide(providers: &mut ty::maps::Providers) {
         is_sized_raw,
         is_freeze_raw,
         needs_drop_raw,
+        layout_raw,
         ..*providers
     };
 }
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index 1b86085d99d1b..3019165bfbf9a 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -14,7 +14,6 @@ use rustc::hir::def_id::DefId;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, AdtKind, Ty, TyCtxt};
 use rustc::ty::layout::{Layout, Primitive};
-use rustc::traits::Reveal;
 use middle::const_val::ConstVal;
 use rustc_const_eval::ConstContext;
 use util::nodemap::FxHashSet;
@@ -724,12 +723,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
         if let hir::ItemEnum(ref enum_definition, ref gens) = it.node {
             if gens.ty_params.is_empty() {
                 // sizes only make sense for non-generic types
-                let t = cx.tcx.type_of(cx.tcx.hir.local_def_id(it.id));
-                let layout = cx.tcx.infer_ctxt(Reveal::All).enter(|infcx| {
-                    let ty = cx.tcx.erase_regions(&t);
-                    ty.layout(&infcx).unwrap_or_else(|e| {
-                        bug!("failed to get layout for `{}`: {}", t, e)
-                    })
+                let item_def_id = cx.tcx.hir.local_def_id(it.id);
+                let t = cx.tcx.type_of(item_def_id);
+                let param_env = cx.tcx.param_env(item_def_id).reveal_all();
+                let ty = cx.tcx.erase_regions(&t);
+                let layout = ty.layout(cx.tcx, param_env).unwrap_or_else(|e| {
+                    bug!("failed to get layout for `{}`: {}", t, e)
                 });
 
                 if let Layout::General { ref variants, ref size, discr, .. } = *layout {
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index 8842d30b65c27..0ac35a5fdd472 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -547,10 +547,8 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
 fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                           param_env: ty::ParamEnv<'tcx>,
                           ty: Ty<'tcx>) -> Option<u64> {
-    tcx.infer_ctxt(param_env.reveal_all()).enter(|infcx| {
-        ty.layout(&infcx).ok().map(|layout| {
-            layout.size(&tcx.data_layout).bytes()
-        })
+    ty.layout(tcx, param_env).ok().map(|layout| {
+        layout.size(&tcx.data_layout).bytes()
     })
 }
 
diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs
index 56f51cae147ad..1a13c8e6f3def 100644
--- a/src/librustc_trans/context.rs
+++ b/src/librustc_trans/context.rs
@@ -25,10 +25,10 @@ use type_::Type;
 use rustc_data_structures::base_n;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TyCtxt};
-use rustc::ty::layout::{LayoutTyper, TyLayout};
-use rustc::session::config::{self, NoDebugInfo};
-use rustc::session::Session;
-use rustc::util::nodemap::{NodeSet, DefIdMap, FxHashMap};
+use rustc::ty::layout::{LayoutCx, LayoutError, LayoutTyper, TyLayout};
+use session::config::{self, NoDebugInfo};
+use session::Session;
+use util::nodemap::{NodeSet, DefIdMap, FxHashMap};
 
 use std::ffi::{CStr, CString};
 use std::cell::{Cell, RefCell};
@@ -709,41 +709,27 @@ impl<'a, 'tcx> ty::layout::HasDataLayout for &'a SharedCrateContext<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a SharedCrateContext<'a, 'tcx> {
-    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
-        self.tcx
-    }
-}
-
 impl<'a, 'tcx> ty::layout::HasDataLayout for &'a CrateContext<'a, 'tcx> {
     fn data_layout(&self) -> &ty::layout::TargetDataLayout {
         &self.shared.tcx.data_layout
     }
 }
 
-impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a CrateContext<'a, 'tcx> {
-    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
-        self.shared.tcx
-    }
-}
-
 impl<'a, 'tcx> LayoutTyper<'tcx> for &'a SharedCrateContext<'a, 'tcx> {
     type TyLayout = TyLayout<'tcx>;
 
-    fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
-        if let Some(&layout) = self.tcx().layout_cache.borrow().get(&ty) {
-            return TyLayout { ty: ty, layout: layout, variant_index: None };
-        }
+    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
+        self.tcx
+    }
 
-        self.tcx().infer_ctxt(traits::Reveal::All).enter(|infcx| {
-            infcx.layout_of(ty).unwrap_or_else(|e| {
-                match e {
-                    ty::layout::LayoutError::SizeOverflow(_) =>
-                        self.sess().fatal(&e.to_string()),
-                    _ => bug!("failed to get layout for `{}`: {}", ty, e)
-                }
+    fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
+        let param_env = ty::ParamEnv::empty(traits::Reveal::All);
+        LayoutCx::new(self.tcx, param_env)
+            .layout_of(ty)
+            .unwrap_or_else(|e| match e {
+                LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()),
+                _ => bug!("failed to get layout for `{}`: {}", ty, e)
             })
-        })
     }
 
     fn normalize_projections(self, ty: Ty<'tcx>) -> Ty<'tcx> {
@@ -754,6 +740,10 @@ impl<'a, 'tcx> LayoutTyper<'tcx> for &'a SharedCrateContext<'a, 'tcx> {
 impl<'a, 'tcx> LayoutTyper<'tcx> for &'a CrateContext<'a, 'tcx> {
     type TyLayout = TyLayout<'tcx>;
 
+    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
+        self.shared.tcx
+    }
+
     fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
         self.shared.layout_of(ty)
     }
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index f473d957a9031..367f0398fa838 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -46,15 +46,13 @@ pub fn needs_drop_glue<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'tcx>
         ty::TyAdt(def, _) if def.is_box() => {
             let typ = t.boxed_ty();
             if !scx.type_needs_drop(typ) && scx.type_is_sized(typ) {
-                scx.tcx().infer_ctxt(traits::Reveal::All).enter(|infcx| {
-                    let layout = t.layout(&infcx).unwrap();
-                    if layout.size(scx).bytes() == 0 {
-                        // `Box<ZeroSizeType>` does not allocate.
-                        false
-                    } else {
-                        true
-                    }
-                })
+                let layout = t.layout(scx.tcx(), ty::ParamEnv::empty(traits::Reveal::All)).unwrap();
+                if layout.size(scx).bytes() == 0 {
+                    // `Box<ZeroSizeType>` does not allocate.
+                    false
+                } else {
+                    true
+                }
             } else {
                 true
             }

From 5fb0f0dc2e7f50b12272e274b084c16b048510db Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Tue, 23 May 2017 04:19:47 -0400
Subject: [PATCH 3/8] strip param-env from infcx

---
 src/librustc/infer/combine.rs                 |   2 +
 src/librustc/infer/mod.rs                     | 186 ++++++++++--------
 src/librustc/infer/sub.rs                     |   1 +
 src/librustc/lib.rs                           |   2 +-
 src/librustc/middle/expr_use_visitor.rs       |  28 ++-
 src/librustc/traits/coherence.rs              |  45 ++++-
 src/librustc/traits/error_reporting.rs        |  19 +-
 src/librustc/traits/fulfill.rs                |  18 +-
 src/librustc/traits/mod.rs                    |  77 ++++----
 src/librustc/traits/project.rs                |  52 +++--
 src/librustc/traits/select.rs                 | 116 +++++++----
 src/librustc/traits/specialize/mod.rs         |  34 ++--
 .../traits/specialize/specialization_graph.rs |   4 +-
 src/librustc/traits/structural_impls.rs       |   1 +
 src/librustc/traits/trans/mod.rs              |   4 +-
 src/librustc/traits/util.rs                   |  24 ++-
 src/librustc/ty/context.rs                    |  31 +++
 src/librustc/ty/mod.rs                        |  23 ---
 src/librustc/ty/structural_impls.rs           |  25 +++
 src/librustc/ty/util.rs                       |  35 ++--
 src/librustc/ty/wf.rs                         |  37 +++-
 src/librustc_borrowck/borrowck/check_loans.rs |   8 +-
 .../borrowck/gather_loans/mod.rs              |   7 +-
 src/librustc_borrowck/borrowck/mod.rs         |   2 +-
 src/librustc_const_eval/check_match.rs        |   4 +-
 src/librustc_const_eval/eval.rs               |   4 +-
 src/librustc_lint/builtin.rs                  |   5 +-
 src/librustc_mir/build/mod.rs                 |   3 +-
 src/librustc_mir/hair/cx/mod.rs               |  12 +-
 src/librustc_mir/transform/qualify_consts.rs  |   7 +-
 src/librustc_mir/transform/type_check.rs      |  21 +-
 src/librustc_passes/consts.rs                 |   4 +-
 src/librustc_trans/context.rs                 |   6 +-
 src/librustc_typeck/check/autoderef.rs        |   5 +-
 src/librustc_typeck/check/cast.rs             |   2 +-
 src/librustc_typeck/check/closure.rs          |   6 +-
 src/librustc_typeck/check/coercion.rs         |  23 ++-
 src/librustc_typeck/check/compare_method.rs   |  39 ++--
 src/librustc_typeck/check/demand.rs           |   4 +-
 src/librustc_typeck/check/dropck.rs           |   6 +-
 src/librustc_typeck/check/method/confirm.rs   |   2 +-
 src/librustc_typeck/check/method/mod.rs       |  13 +-
 src/librustc_typeck/check/method/probe.rs     |  13 +-
 src/librustc_typeck/check/method/suggest.rs   |   5 +-
 src/librustc_typeck/check/mod.rs              |  57 ++++--
 src/librustc_typeck/check/op.rs               |  12 +-
 src/librustc_typeck/check/regionck.rs         |   4 +-
 src/librustc_typeck/check/upvar.rs            |   2 +
 src/librustc_typeck/check/wfcheck.rs          |  14 +-
 src/librustc_typeck/coherence/builtin.rs      |  14 +-
 .../coherence/inherent_impls_overlap.rs       |   4 +-
 src/librustc_typeck/lib.rs                    |   5 +-
 52 files changed, 694 insertions(+), 383 deletions(-)

diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index aabb6aff55140..14920b8b668ec 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -55,6 +55,7 @@ pub struct CombineFields<'infcx, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> {
     pub infcx: &'infcx InferCtxt<'infcx, 'gcx, 'tcx>,
     pub trace: TypeTrace<'tcx>,
     pub cause: Option<ty::relate::Cause>,
+    pub param_env: ty::ParamEnv<'tcx>,
     pub obligations: PredicateObligations<'tcx>,
 }
 
@@ -215,6 +216,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
 
         if needs_wf {
             self.obligations.push(Obligation::new(self.trace.cause.clone(),
+                                                  self.param_env,
                                                   ty::Predicate::WellFormed(b_ty)));
         }
 
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 5dbf30d8fa8a4..a16f7fb208175 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -161,8 +161,6 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     // For region variables.
     region_vars: RegionVarBindings<'a, 'gcx, 'tcx>,
 
-    pub param_env: ty::ParamEnv<'gcx>,
-
     /// Caches the results of trait selection. This cache is used
     /// for things that have to do with the parameters in scope.
     pub selection_cache: traits::SelectionCache<'tcx>,
@@ -400,55 +398,39 @@ impl fmt::Display for FixupError {
 pub trait InferEnv<'a, 'tcx> {
     fn to_parts(self, tcx: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
-                    Option<ty::TypeckTables<'tcx>>,
-                    ty::ParamEnv<'tcx>);
-}
-
-impl<'a, 'tcx> InferEnv<'a, 'tcx> for Reveal {
-    fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>)
-                -> (Option<&'a ty::TypeckTables<'tcx>>,
-                    Option<ty::TypeckTables<'tcx>>,
-                    ty::ParamEnv<'tcx>) {
-        (None, None, ty::ParamEnv::empty(self))
-    }
+                    Option<ty::TypeckTables<'tcx>>);
 }
 
-impl<'a, 'tcx> InferEnv<'a, 'tcx> for ty::ParamEnv<'tcx> {
+impl<'a, 'tcx> InferEnv<'a, 'tcx> for () {
     fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
-                    Option<ty::TypeckTables<'tcx>>,
-                    ty::ParamEnv<'tcx>) {
-        (None, None, self)
+                    Option<ty::TypeckTables<'tcx>>) {
+        (None, None)
     }
 }
 
-impl<'a, 'tcx> InferEnv<'a, 'tcx> for (&'a ty::TypeckTables<'tcx>, ty::ParamEnv<'tcx>) {
+impl<'a, 'tcx> InferEnv<'a, 'tcx> for &'a ty::TypeckTables<'tcx> {
     fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
-                    Option<ty::TypeckTables<'tcx>>,
-                    ty::ParamEnv<'tcx>) {
-        (Some(self.0), None, self.1)
+                    Option<ty::TypeckTables<'tcx>>) {
+        (Some(self), None)
     }
 }
 
-impl<'a, 'tcx> InferEnv<'a, 'tcx> for (ty::TypeckTables<'tcx>, ty::ParamEnv<'tcx>) {
+impl<'a, 'tcx> InferEnv<'a, 'tcx> for ty::TypeckTables<'tcx> {
     fn to_parts(self, _: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
-                    Option<ty::TypeckTables<'tcx>>,
-                    ty::ParamEnv<'tcx>) {
-        (None, Some(self.0), self.1)
+                    Option<ty::TypeckTables<'tcx>>) {
+        (None, Some(self))
     }
 }
 
 impl<'a, 'tcx> InferEnv<'a, 'tcx> for hir::BodyId {
     fn to_parts(self, tcx: TyCtxt<'a, 'tcx, 'tcx>)
                 -> (Option<&'a ty::TypeckTables<'tcx>>,
-                    Option<ty::TypeckTables<'tcx>>,
-                    ty::ParamEnv<'tcx>) {
+                    Option<ty::TypeckTables<'tcx>>) {
         let def_id = tcx.hir.body_owner_def_id(self);
-        (Some(tcx.typeck_tables_of(def_id)),
-         None,
-         tcx.param_env(def_id))
+        (Some(tcx.typeck_tables_of(def_id)), None)
     }
 }
 
@@ -460,18 +442,16 @@ pub struct InferCtxtBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     arena: DroplessArena,
     fresh_tables: Option<RefCell<ty::TypeckTables<'tcx>>>,
     tables: Option<&'a ty::TypeckTables<'gcx>>,
-    param_env: ty::ParamEnv<'gcx>,
 }
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
     pub fn infer_ctxt<E: InferEnv<'a, 'gcx>>(self, env: E) -> InferCtxtBuilder<'a, 'gcx, 'tcx> {
-        let (tables, fresh_tables, param_env) = env.to_parts(self);
+        let (tables, fresh_tables) = env.to_parts(self);
         InferCtxtBuilder {
             global_tcx: self,
             arena: DroplessArena::new(),
             fresh_tables: fresh_tables.map(RefCell::new),
             tables: tables,
-            param_env: param_env,
         }
     }
 
@@ -480,7 +460,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
     /// If any inference functionality is used, ICEs will occur.
     pub fn borrowck_fake_infer_ctxt(self, body: hir::BodyId)
                                     -> InferCtxt<'a, 'gcx, 'gcx> {
-        let (tables, _, param_env) = body.to_parts(self);
+        let (tables, _) = body.to_parts(self);
         InferCtxt {
             tcx: self,
             tables: InferTables::Interned(tables.unwrap()),
@@ -488,7 +468,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
             int_unification_table: RefCell::new(UnificationTable::new()),
             float_unification_table: RefCell::new(UnificationTable::new()),
             region_vars: RegionVarBindings::new(self),
-            param_env: param_env,
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
             projection_cache: RefCell::new(traits::ProjectionCache::new()),
@@ -509,7 +488,6 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
             ref arena,
             ref fresh_tables,
             tables,
-            param_env,
         } = *self;
         let tables = tables.map(InferTables::Interned).unwrap_or_else(|| {
             fresh_tables.as_ref().map_or(InferTables::Missing, InferTables::InProgress)
@@ -522,7 +500,6 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
             int_unification_table: RefCell::new(UnificationTable::new()),
             float_unification_table: RefCell::new(UnificationTable::new()),
             region_vars: RegionVarBindings::new(tcx),
-            param_env: param_env,
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
             reported_trait_errors: RefCell::new(FxHashSet()),
@@ -563,7 +540,10 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> {
 /// Helper trait for shortening the lifetimes inside a
 /// value for post-type-checking normalization.
 pub trait TransNormalize<'gcx>: TypeFoldable<'gcx> {
-    fn trans_normalize<'a, 'tcx>(&self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) -> Self;
+    fn trans_normalize<'a, 'tcx>(&self,
+                                 infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                 param_env: ty::ParamEnv<'tcx>)
+                                 -> Self;
 }
 
 macro_rules! items { ($($item:item)+) => ($($item)+) }
@@ -571,9 +551,10 @@ macro_rules! impl_trans_normalize {
     ($lt_gcx:tt, $($ty:ty),+) => {
         items!($(impl<$lt_gcx> TransNormalize<$lt_gcx> for $ty {
             fn trans_normalize<'a, 'tcx>(&self,
-                                         infcx: &InferCtxt<'a, $lt_gcx, 'tcx>)
+                                         infcx: &InferCtxt<'a, $lt_gcx, 'tcx>,
+                                         param_env: ty::ParamEnv<'tcx>)
                                          -> Self {
-                infcx.normalize_projections_in(self)
+                infcx.normalize_projections_in(param_env, self)
             }
         })+);
     }
@@ -590,13 +571,16 @@ impl_trans_normalize!('gcx,
 );
 
 impl<'gcx> TransNormalize<'gcx> for LvalueTy<'gcx> {
-    fn trans_normalize<'a, 'tcx>(&self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) -> Self {
+    fn trans_normalize<'a, 'tcx>(&self,
+                                 infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                 param_env: ty::ParamEnv<'tcx>)
+                                 -> Self {
         match *self {
-            LvalueTy::Ty { ty } => LvalueTy::Ty { ty: ty.trans_normalize(infcx) },
+            LvalueTy::Ty { ty } => LvalueTy::Ty { ty: ty.trans_normalize(infcx, param_env) },
             LvalueTy::Downcast { adt_def, substs, variant_index } => {
                 LvalueTy::Downcast {
                     adt_def: adt_def,
-                    substs: substs.trans_normalize(infcx),
+                    substs: substs.trans_normalize(infcx, param_env),
                     variant_index: variant_index
                 }
             }
@@ -618,19 +602,23 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
         self.normalize_associated_type(&value)
     }
 
+    /// Fully normalizes any associated types in `value`, using an
+    /// empty environment and `Reveal::All` mode (therefore, suitable
+    /// only for monomorphized code during trans, basically).
     pub fn normalize_associated_type<T>(self, value: &T) -> T
         where T: TransNormalize<'tcx>
     {
         debug!("normalize_associated_type(t={:?})", value);
 
+        let param_env = ty::ParamEnv::empty(Reveal::All);
         let value = self.erase_regions(value);
 
         if !value.has_projection_types() {
             return value;
         }
 
-        self.infer_ctxt(Reveal::All).enter(|infcx| {
-            value.trans_normalize(&infcx)
+        self.infer_ctxt(()).enter(|infcx| {
+            value.trans_normalize(&infcx, param_env)
         })
     }
 
@@ -651,20 +639,20 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
             return value;
         }
 
-        self.infer_ctxt(env.reveal_all()).enter(|infcx| {
-            value.trans_normalize(&infcx)
+        self.infer_ctxt(()).enter(|infcx| {
+            value.trans_normalize(&infcx, env.reveal_all())
        })
     }
 }
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
-    fn normalize_projections_in<T>(&self, value: &T) -> T::Lifted
+    fn normalize_projections_in<T>(&self, param_env: ty::ParamEnv<'tcx>, value: &T) -> T::Lifted
         where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
     {
         let mut selcx = traits::SelectionContext::new(self);
         let cause = traits::ObligationCause::dummy();
         let traits::Normalized { value: result, obligations } =
-            traits::normalize(&mut selcx, cause, value);
+            traits::normalize(&mut selcx, param_env, cause, value);
 
         debug!("normalize_projections_in: result={:?} obligations={:?}",
                 result, obligations);
@@ -803,48 +791,69 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         return variables;
     }
 
-    fn combine_fields(&'a self, trace: TypeTrace<'tcx>)
+    fn combine_fields(&'a self, trace: TypeTrace<'tcx>, param_env: ty::ParamEnv<'tcx>)
                       -> CombineFields<'a, 'gcx, 'tcx> {
         CombineFields {
             infcx: self,
             trace: trace,
             cause: None,
+            param_env,
             obligations: PredicateObligations::new(),
         }
     }
 
-    pub fn equate<T>(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>, a: &T, b: &T)
+    pub fn equate<T>(&'a self,
+                     a_is_expected: bool,
+                     trace: TypeTrace<'tcx>,
+                     param_env: ty::ParamEnv<'tcx>,
+                     a: &T,
+                     b: &T)
         -> InferResult<'tcx, T>
         where T: Relate<'tcx>
     {
-        let mut fields = self.combine_fields(trace);
+        let mut fields = self.combine_fields(trace, param_env);
         let result = fields.equate(a_is_expected).relate(a, b);
         result.map(move |t| InferOk { value: t, obligations: fields.obligations })
     }
 
-    pub fn sub<T>(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>, a: &T, b: &T)
+    pub fn sub<T>(&'a self,
+                  a_is_expected: bool,
+                  trace: TypeTrace<'tcx>,
+                  param_env: ty::ParamEnv<'tcx>,
+                  a: &T,
+                  b: &T)
         -> InferResult<'tcx, T>
         where T: Relate<'tcx>
     {
-        let mut fields = self.combine_fields(trace);
+        let mut fields = self.combine_fields(trace, param_env);
         let result = fields.sub(a_is_expected).relate(a, b);
         result.map(move |t| InferOk { value: t, obligations: fields.obligations })
     }
 
-    pub fn lub<T>(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>, a: &T, b: &T)
+    pub fn lub<T>(&'a self,
+                  a_is_expected: bool,
+                  trace: TypeTrace<'tcx>,
+                  param_env: ty::ParamEnv<'tcx>,
+                  a: &T,
+                  b: &T)
         -> InferResult<'tcx, T>
         where T: Relate<'tcx>
     {
-        let mut fields = self.combine_fields(trace);
+        let mut fields = self.combine_fields(trace, param_env);
         let result = fields.lub(a_is_expected).relate(a, b);
         result.map(move |t| InferOk { value: t, obligations: fields.obligations })
     }
 
-    pub fn glb<T>(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>, a: &T, b: &T)
+    pub fn glb<T>(&'a self,
+                  a_is_expected: bool,
+                  trace: TypeTrace<'tcx>,
+                  param_env: ty::ParamEnv<'tcx>,
+                  a: &T,
+                  b: &T)
         -> InferResult<'tcx, T>
         where T: Relate<'tcx>
     {
-        let mut fields = self.combine_fields(trace);
+        let mut fields = self.combine_fields(trace, param_env);
         let result = fields.glb(a_is_expected).relate(a, b);
         result.map(move |t| InferOk { value: t, obligations: fields.obligations })
     }
@@ -1011,18 +1020,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn sub_types(&self,
                      a_is_expected: bool,
                      cause: &ObligationCause<'tcx>,
+                     param_env: ty::ParamEnv<'tcx>,
                      a: Ty<'tcx>,
                      b: Ty<'tcx>)
-        -> InferResult<'tcx, ()>
+                     -> InferResult<'tcx, ()>
     {
         debug!("sub_types({:?} <: {:?})", a, b);
         self.commit_if_ok(|_| {
             let trace = TypeTrace::types(cause, a_is_expected, a, b);
-            self.sub(a_is_expected, trace, &a, &b).map(|ok| ok.unit())
+            self.sub(a_is_expected, trace, param_env, &a, &b).map(|ok| ok.unit())
         })
     }
 
     pub fn can_sub_types(&self,
+                         param_env: ty::ParamEnv<'tcx>,
                          a: Ty<'tcx>,
                          b: Ty<'tcx>)
                          -> UnitResult<'tcx>
@@ -1030,7 +1041,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.probe(|_| {
             let origin = &ObligationCause::dummy();
             let trace = TypeTrace::types(origin, true, a, b);
-            self.sub(true, trace, &a, &b).map(|InferOk { obligations: _, .. }| {
+            self.sub(true, trace, param_env, &a, &b).map(|InferOk { obligations: _, .. }| {
                 // Ignore obligations, since we are unrolling
                 // everything anyway.
             })
@@ -1040,21 +1051,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn eq_types(&self,
                     a_is_expected: bool,
                     cause: &ObligationCause<'tcx>,
+                    param_env: ty::ParamEnv<'tcx>,
                     a: Ty<'tcx>,
                     b: Ty<'tcx>)
         -> InferResult<'tcx, ()>
     {
         self.commit_if_ok(|_| {
             let trace = TypeTrace::types(cause, a_is_expected, a, b);
-            self.equate(a_is_expected, trace, &a, &b).map(|ok| ok.unit())
+            self.equate(a_is_expected, trace, param_env, &a, &b).map(|ok| ok.unit())
         })
     }
 
     pub fn eq_trait_refs(&self,
-                          a_is_expected: bool,
-                          cause: &ObligationCause<'tcx>,
-                          a: ty::TraitRef<'tcx>,
-                          b: ty::TraitRef<'tcx>)
+                         a_is_expected: bool,
+                         cause: &ObligationCause<'tcx>,
+                         param_env: ty::ParamEnv<'tcx>,
+                         a: ty::TraitRef<'tcx>,
+                         b: ty::TraitRef<'tcx>)
         -> InferResult<'tcx, ()>
     {
         debug!("eq_trait_refs({:?} = {:?})", a, b);
@@ -1063,21 +1076,24 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 cause: cause.clone(),
                 values: TraitRefs(ExpectedFound::new(a_is_expected, a, b))
             };
-            self.equate(a_is_expected, trace, &a, &b).map(|ok| ok.unit())
+            self.equate(a_is_expected, trace, param_env, &a, &b).map(|ok| ok.unit())
         })
     }
 
     pub fn eq_impl_headers(&self,
                            a_is_expected: bool,
                            cause: &ObligationCause<'tcx>,
+                           param_env: ty::ParamEnv<'tcx>,
                            a: &ty::ImplHeader<'tcx>,
                            b: &ty::ImplHeader<'tcx>)
                            -> InferResult<'tcx, ()>
     {
         debug!("eq_impl_header({:?} = {:?})", a, b);
         match (a.trait_ref, b.trait_ref) {
-            (Some(a_ref), Some(b_ref)) => self.eq_trait_refs(a_is_expected, cause, a_ref, b_ref),
-            (None, None) => self.eq_types(a_is_expected, cause, a.self_ty, b.self_ty),
+            (Some(a_ref), Some(b_ref)) =>
+                self.eq_trait_refs(a_is_expected, cause, param_env, a_ref, b_ref),
+            (None, None) =>
+                self.eq_types(a_is_expected, cause, param_env, a.self_ty, b.self_ty),
             _ => bug!("mk_eq_impl_headers given mismatched impl kinds"),
         }
     }
@@ -1085,6 +1101,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn sub_poly_trait_refs(&self,
                                a_is_expected: bool,
                                cause: ObligationCause<'tcx>,
+                               param_env: ty::ParamEnv<'tcx>,
                                a: ty::PolyTraitRef<'tcx>,
                                b: ty::PolyTraitRef<'tcx>)
         -> InferResult<'tcx, ()>
@@ -1095,7 +1112,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 cause: cause,
                 values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b))
             };
-            self.sub(a_is_expected, trace, &a, &b).map(|ok| ok.unit())
+            self.sub(a_is_expected, trace, param_env, &a, &b).map(|ok| ok.unit())
         })
     }
 
@@ -1109,6 +1126,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
     pub fn equality_predicate(&self,
                               cause: &ObligationCause<'tcx>,
+                              param_env: ty::ParamEnv<'tcx>,
                               predicate: &ty::PolyEquatePredicate<'tcx>)
         -> InferResult<'tcx, ()>
     {
@@ -1116,7 +1134,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             let (ty::EquatePredicate(a, b), skol_map) =
                 self.skolemize_late_bound_regions(predicate, snapshot);
             let cause_span = cause.span;
-            let eqty_ok = self.eq_types(false, cause, a, b)?;
+            let eqty_ok = self.eq_types(false, cause, param_env, a, b)?;
             self.leak_check(false, cause_span, &skol_map, snapshot)?;
             self.pop_skolemized(skol_map, snapshot);
             Ok(eqty_ok.unit())
@@ -1125,6 +1143,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
     pub fn subtype_predicate(&self,
                              cause: &ObligationCause<'tcx>,
+                             param_env: ty::ParamEnv<'tcx>,
                              predicate: &ty::PolySubtypePredicate<'tcx>)
         -> Option<InferResult<'tcx, ()>>
     {
@@ -1153,7 +1172,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 self.skolemize_late_bound_regions(predicate, snapshot);
 
             let cause_span = cause.span;
-            let ok = self.sub_types(a_is_expected, cause, a, b)?;
+            let ok = self.sub_types(a_is_expected, cause, param_env, a, b)?;
             self.leak_check(false, cause_span, &skol_map, snapshot)?;
             self.pop_skolemized(skol_map, snapshot);
             Ok(ok.unit())
@@ -1555,6 +1574,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// details.
     pub fn match_poly_projection_predicate(&self,
                                            cause: ObligationCause<'tcx>,
+                                           param_env: ty::ParamEnv<'tcx>,
                                            match_a: ty::PolyProjectionPredicate<'tcx>,
                                            match_b: ty::TraitRef<'tcx>)
                                            -> InferResult<'tcx, HrMatchResult<Ty<'tcx>>>
@@ -1567,7 +1587,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         };
 
         let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref, p.ty));
-        let mut combine = self.combine_fields(trace);
+        let mut combine = self.combine_fields(trace, param_env);
         let result = combine.higher_ranked_match(span, &match_pair, &match_b, true)?;
         Ok(InferOk { value: result, obligations: combine.obligations })
     }
@@ -1586,7 +1606,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.region_vars.verify_generic_bound(origin, kind, a, bound);
     }
 
-    pub fn can_equate<T>(&self, a: &T, b: &T) -> UnitResult<'tcx>
+    pub fn can_equate<T>(&self, param_env: ty::ParamEnv<'tcx>, a: &T, b: &T) -> UnitResult<'tcx>
         where T: Relate<'tcx> + fmt::Debug
     {
         debug!("can_equate({:?}, {:?})", a, b);
@@ -1596,7 +1616,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             // generic so we don't have to do anything quite this
             // terrible.
             let trace = TypeTrace::dummy(self.tcx);
-            self.equate(true, trace, a, b).map(|InferOk { obligations: _, .. }| {
+            self.equate(true, trace, param_env, a, b).map(|InferOk { obligations: _, .. }| {
                 // We can intentionally ignore obligations here, since
                 // this is part of a simple test for general
                 // "equatability". However, it's not entirely clear
@@ -1617,9 +1637,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.resolve_type_vars_or_error(&ty)
     }
 
-    pub fn type_moves_by_default(&self, ty: Ty<'tcx>, span: Span) -> bool {
+    pub fn type_moves_by_default(&self,
+                                 param_env: ty::ParamEnv<'tcx>,
+                                 ty: Ty<'tcx>,
+                                 span: Span)
+                                 -> bool {
         let ty = self.resolve_type_vars_if_possible(&ty);
-        if let Some(ty) = self.tcx.lift_to_global(&ty) {
+        if let Some((param_env, ty)) = self.tcx.lift_to_global(&(param_env, ty)) {
             // Even if the type may have no inference variables, during
             // type-checking closure types are in local tables only.
             let local_closures = match self.tables {
@@ -1627,7 +1651,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 _ => false
             };
             if !local_closures {
-                return ty.moves_by_default(self.tcx.global_tcx(), self.param_env(), span);
+                return ty.moves_by_default(self.tcx.global_tcx(), param_env, span);
             }
         }
 
@@ -1637,17 +1661,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         // rightly refuses to work with inference variables, but
         // moves_by_default has a cache, which we want to use in other
         // cases.
-        !traits::type_known_to_meet_bound(self, ty, copy_def_id, span)
+        !traits::type_known_to_meet_bound(self, param_env, ty, copy_def_id, span)
     }
 
     pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture<'tcx>> {
         self.tables.borrow().upvar_capture_map.get(&upvar_id).cloned()
     }
 
-    pub fn param_env(&self) -> ty::ParamEnv<'gcx> {
-        self.param_env
-    }
-
     pub fn closure_kind(&self,
                         def_id: DefId)
                         -> Option<ty::ClosureKind>
diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs
index 487195fdfae9f..4056999681352 100644
--- a/src/librustc/infer/sub.rs
+++ b/src/librustc/infer/sub.rs
@@ -96,6 +96,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                 self.fields.obligations.push(
                     Obligation::new(
                         self.fields.trace.cause.clone(),
+                        self.fields.param_env,
                         ty::Predicate::Subtype(
                             ty::Binder(ty::SubtypePredicate {
                                 a_is_expected: self.a_is_expected,
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 2a877aca53b7c..2216103636fb6 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -47,7 +47,7 @@
 #![cfg_attr(stage0, feature(staged_api))]
 #![cfg_attr(stage0, feature(loop_break_value))]
 
-#![recursion_limit="192"]
+#![recursion_limit="256"]
 
 extern crate arena;
 extern crate core;
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index bb56439a157fe..7ab534605c251 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -242,6 +242,7 @@ impl OverloadedCallType {
 pub struct ExprUseVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     mc: mc::MemCategorizationContext<'a, 'gcx, 'tcx>,
     delegate: &'a mut Delegate<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 // If the TYPER results in an error, it's because the type check
@@ -266,24 +267,28 @@ macro_rules! return_if_err {
 impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
     pub fn new(delegate: &'a mut (Delegate<'tcx>+'a),
                region_maps: &'a RegionMaps,
-               infcx: &'a InferCtxt<'a, 'gcx, 'tcx>)
+               infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+               param_env: ty::ParamEnv<'tcx>)
                -> Self
     {
         ExprUseVisitor::with_options(delegate,
                                      infcx,
+                                     param_env,
                                      region_maps,
                                      mc::MemCategorizationOptions::default())
     }
 
     pub fn with_options(delegate: &'a mut (Delegate<'tcx>+'a),
                         infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+                        param_env: ty::ParamEnv<'tcx>,
                         region_maps: &'a RegionMaps,
                         options: mc::MemCategorizationOptions)
                -> Self
     {
         ExprUseVisitor {
             mc: mc::MemCategorizationContext::with_options(infcx, region_maps, options),
-            delegate: delegate
+            delegate,
+            param_env,
         }
     }
 
@@ -318,7 +323,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         debug!("delegate_consume(consume_id={}, cmt={:?})",
                consume_id, cmt);
 
-        let mode = copy_or_move(self.mc.infcx, &cmt, DirectRefMove);
+        let mode = copy_or_move(self.mc.infcx, self.param_env, &cmt, DirectRefMove);
         self.delegate.consume(consume_id, consume_span, cmt, mode);
     }
 
@@ -797,7 +802,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                 PatKind::Binding(hir::BindByRef(..), ..) =>
                     mode.lub(BorrowingMatch),
                 PatKind::Binding(hir::BindByValue(..), ..) => {
-                    match copy_or_move(self.mc.infcx, &cmt_pat, PatBindingMove) {
+                    match copy_or_move(self.mc.infcx, self.param_env, &cmt_pat, PatBindingMove) {
                         Copy => mode.lub(CopyingMatch),
                         Move(..) => mode.lub(MovingMatch),
                     }
@@ -813,10 +818,9 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
     fn walk_pat(&mut self, cmt_discr: mc::cmt<'tcx>, pat: &hir::Pat, match_mode: MatchMode) {
         debug!("walk_pat cmt_discr={:?} pat={:?}", cmt_discr, pat);
 
-        let tcx = &self.tcx();
-        let mc = &self.mc;
+        let tcx = self.tcx();
         let infcx = self.mc.infcx;
-        let delegate = &mut self.delegate;
+        let ExprUseVisitor { ref mc, ref mut delegate, param_env } = *self;
         return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| {
             if let PatKind::Binding(bmode, def_id, ..) = pat.node {
                 debug!("binding cmt_pat={:?} pat={:?} match_mode={:?}", cmt_pat, pat, match_mode);
@@ -840,7 +844,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                         }
                     }
                     hir::BindByValue(..) => {
-                        let mode = copy_or_move(infcx, &cmt_pat, PatBindingMove);
+                        let mode = copy_or_move(infcx, param_env, &cmt_pat, PatBindingMove);
                         debug!("walk_pat binding consuming pat");
                         delegate.consume_pat(pat, cmt_pat, mode);
                     }
@@ -899,7 +903,10 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                                                                    freevar.def));
                 match upvar_capture {
                     ty::UpvarCapture::ByValue => {
-                        let mode = copy_or_move(self.mc.infcx, &cmt_var, CaptureMove);
+                        let mode = copy_or_move(self.mc.infcx,
+                                                self.param_env,
+                                                &cmt_var,
+                                                CaptureMove);
                         self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode);
                     }
                     ty::UpvarCapture::ByRef(upvar_borrow) => {
@@ -929,11 +936,12 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
 }
 
 fn copy_or_move<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                param_env: ty::ParamEnv<'tcx>,
                                 cmt: &mc::cmt<'tcx>,
                                 move_reason: MoveReason)
                                 -> ConsumeMode
 {
-    if infcx.type_moves_by_default(cmt.ty, cmt.span) {
+    if infcx.type_moves_by_default(param_env, cmt.ty, cmt.span) {
         Move(move_reason)
     } else {
         Copy
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs
index a943ef30e534b..c7d069053e9d0 100644
--- a/src/librustc/traits/coherence.rs
+++ b/src/librustc/traits/coherence.rs
@@ -10,10 +10,11 @@
 
 //! See `README.md` for high-level documentation
 
-use super::{SelectionContext, Obligation, ObligationCause};
-
 use hir::def_id::{DefId, LOCAL_CRATE};
+use syntax_pos::DUMMY_SP;
+use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause, Reveal};
 use ty::{self, Ty, TyCtxt};
+use ty::subst::Subst;
 
 use infer::{InferCtxt, InferOk};
 
@@ -37,6 +38,28 @@ pub fn overlapping_impls<'cx, 'gcx, 'tcx>(infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
     overlap(selcx, impl1_def_id, impl2_def_id)
 }
 
+fn with_fresh_ty_vars<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
+                                       param_env: ty::ParamEnv<'tcx>,
+                                       impl_def_id: DefId)
+                                       -> ty::ImplHeader<'tcx>
+{
+    let tcx = selcx.tcx();
+    let impl_substs = selcx.infcx().fresh_substs_for_item(DUMMY_SP, impl_def_id);
+
+    let header = ty::ImplHeader {
+        impl_def_id: impl_def_id,
+        self_ty: tcx.type_of(impl_def_id),
+        trait_ref: tcx.impl_trait_ref(impl_def_id),
+        predicates: tcx.predicates_of(impl_def_id).predicates
+    }.subst(tcx, impl_substs);
+
+    let Normalized { value: mut header, obligations } =
+        traits::normalize(selcx, param_env, ObligationCause::dummy(), &header);
+
+    header.predicates.extend(obligations.into_iter().map(|o| o.predicate));
+    header
+}
+
 /// Can both impl `a` and impl `b` be satisfied by a common type (including
 /// `where` clauses)? If so, returns an `ImplHeader` that unifies the two impls.
 fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
@@ -48,17 +71,24 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
            a_def_id,
            b_def_id);
 
-    let a_impl_header = ty::ImplHeader::with_fresh_ty_vars(selcx, a_def_id);
-    let b_impl_header = ty::ImplHeader::with_fresh_ty_vars(selcx, b_def_id);
+    // For the purposes of this check, we don't bring any skolemized
+    // types into scope; instead, we replace the generic types with
+    // fresh type variables, and hence we do our evaluations in an
+    // empty environment.
+    let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+
+    let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id);
+    let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id);
 
     debug!("overlap: a_impl_header={:?}", a_impl_header);
     debug!("overlap: b_impl_header={:?}", b_impl_header);
 
     // Do `a` and `b` unify? If not, no overlap.
     let obligations = match selcx.infcx().eq_impl_headers(true,
-                                        &ObligationCause::dummy(),
-                                        &a_impl_header,
-                                        &b_impl_header) {
+                                                          &ObligationCause::dummy(),
+                                                          param_env,
+                                                          &a_impl_header,
+                                                          &b_impl_header) {
         Ok(InferOk { obligations, .. }) => {
             obligations
         }
@@ -75,6 +105,7 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
                      .chain(&b_impl_header.predicates)
                      .map(|p| infcx.resolve_type_vars_if_possible(p))
                      .map(|p| Obligation { cause: ObligationCause::dummy(),
+                                           param_env: param_env,
                                            recursion_depth: 0,
                                            predicate: p })
                      .chain(obligations)
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 3c7761c6cd3bc..6635a1f1f271e 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -179,12 +179,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     data);
                 let normalized = super::normalize_projection_type(
                     &mut selcx,
+                    obligation.param_env,
                     data.projection_ty,
                     obligation.cause.clone(),
                     0
                 );
                 if let Err(error) = self.eq_types(
-                    false, &obligation.cause,
+                    false, &obligation.cause, obligation.param_env,
                     data.ty, normalized.value
                 ) {
                     values = Some(infer::ValuePairs::Types(ExpectedFound {
@@ -251,7 +252,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                        -> Option<DefId>
     {
         let tcx = self.tcx;
-
+        let param_env = obligation.param_env;
         let trait_ref = tcx.erase_late_bound_regions(&trait_ref);
         let trait_self_ty = trait_ref.self_ty();
 
@@ -268,7 +269,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
                 let impl_self_ty = impl_trait_ref.self_ty();
 
-                if let Ok(..) = self.can_equate(&trait_self_ty, &impl_self_ty) {
+                if let Ok(..) = self.can_equate(param_env, &trait_self_ty, &impl_self_ty) {
                     self_match_impls.push(def_id);
 
                     if trait_ref.substs.types().skip(1)
@@ -578,7 +579,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
                         // Try to report a help message
                         if !trait_ref.has_infer_types() &&
-                            self.predicate_can_apply(trait_ref) {
+                            self.predicate_can_apply(obligation.param_env, trait_ref) {
                             // If a where-clause may be useful, remind the
                             // user that they can add it.
                             //
@@ -607,7 +608,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     ty::Predicate::Equate(ref predicate) => {
                         let predicate = self.resolve_type_vars_if_possible(predicate);
                         let err = self.equality_predicate(&obligation.cause,
-                                                            &predicate).err().unwrap();
+                                                          obligation.param_env,
+                                                          &predicate).err().unwrap();
                         struct_span_err!(self.tcx.sess, span, E0278,
                             "the requirement `{}` is not satisfied (`{}`)",
                             predicate, err)
@@ -936,7 +938,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
     /// Returns whether the trait predicate may apply for *some* assignment
     /// to the type parameters.
-    fn predicate_can_apply(&self, pred: ty::PolyTraitRef<'tcx>) -> bool {
+    fn predicate_can_apply(&self,
+                           param_env: ty::ParamEnv<'tcx>,
+                           pred: ty::PolyTraitRef<'tcx>)
+                           -> bool {
         struct ParamToVarFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
             infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
             var_map: FxHashMap<Ty<'tcx>, Ty<'tcx>>
@@ -967,12 +972,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
             let cleaned_pred = super::project::normalize(
                 &mut selcx,
+                param_env,
                 ObligationCause::dummy(),
                 &cleaned_pred
             ).value;
 
             let obligation = Obligation::new(
                 ObligationCause::dummy(),
+                param_env,
                 cleaned_pred.to_predicate()
             );
 
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index e8baaa7ffb26d..daeb96e188d37 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -113,6 +113,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
     /// `projection_ty` again.
     pub fn normalize_projection_type(&mut self,
                                      infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                     param_env: ty::ParamEnv<'tcx>,
                                      projection_ty: ty::ProjectionTy<'tcx>,
                                      cause: ObligationCause<'tcx>)
                                      -> Ty<'tcx>
@@ -125,7 +126,11 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         // FIXME(#20304) -- cache
 
         let mut selcx = SelectionContext::new(infcx);
-        let normalized = project::normalize_projection_type(&mut selcx, projection_ty, cause, 0);
+        let normalized = project::normalize_projection_type(&mut selcx,
+                                                            param_env,
+                                                            projection_ty,
+                                                            cause,
+                                                            0);
 
         for obligation in normalized.obligations {
             self.register_predicate_obligation(infcx, obligation);
@@ -136,8 +141,12 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         normalized.value
     }
 
+    /// Requires that `ty` must implement the trait with `def_id` in
+    /// the given environment. This trait must not have any type
+    /// parameters (except for `Self`).
     pub fn register_bound(&mut self,
                           infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                          param_env: ty::ParamEnv<'tcx>,
                           ty: Ty<'tcx>,
                           def_id: DefId,
                           cause: ObligationCause<'tcx>)
@@ -149,6 +158,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         self.register_predicate_obligation(infcx, Obligation {
             cause: cause,
             recursion_depth: 0,
+            param_env,
             predicate: trait_ref.to_predicate()
         });
     }
@@ -410,7 +420,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
         }
 
         ty::Predicate::Equate(ref binder) => {
-            match selcx.infcx().equality_predicate(&obligation.cause, binder) {
+            match selcx.infcx().equality_predicate(&obligation.cause, obligation.param_env, binder) {
                 Ok(InferOk { obligations, value: () }) => {
                     Ok(Some(obligations))
                 },
@@ -498,7 +508,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
         }
 
         ty::Predicate::WellFormed(ty) => {
-            match ty::wf::obligations(selcx.infcx(), obligation.cause.body_id,
+            match ty::wf::obligations(selcx.infcx(), obligation.param_env, obligation.cause.body_id,
                                       ty, obligation.cause.span) {
                 None => {
                     pending_obligation.stalled_on = vec![ty];
@@ -509,7 +519,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
         }
 
         ty::Predicate::Subtype(ref subtype) => {
-            match selcx.infcx().subtype_predicate(&obligation.cause, subtype) {
+            match selcx.infcx().subtype_predicate(&obligation.cause, obligation.param_env, subtype) {
                 None => {
                     // none means that both are unresolved
                     pending_obligation.stalled_on = vec![subtype.skip_binder().a,
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index fee6ce95a3f7f..c51974e6e6700 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -68,6 +68,7 @@ mod util;
 #[derive(Clone, PartialEq, Eq)]
 pub struct Obligation<'tcx, T> {
     pub cause: ObligationCause<'tcx>,
+    pub param_env: ty::ParamEnv<'tcx>,
     pub recursion_depth: usize,
     pub predicate: T,
 }
@@ -359,10 +360,11 @@ pub struct VtableFnPointerData<'tcx, N> {
 
 /// Creates predicate obligations from the generic bounds.
 pub fn predicates_for_generics<'tcx>(cause: ObligationCause<'tcx>,
+                                     param_env: ty::ParamEnv<'tcx>,
                                      generic_bounds: &ty::InstantiatedPredicates<'tcx>)
                                      -> PredicateObligations<'tcx>
 {
-    util::predicates_for_generics(cause, 0, generic_bounds)
+    util::predicates_for_generics(cause, 0, param_env, generic_bounds)
 }
 
 /// Determines whether the type `ty` is known to meet `bound` and
@@ -371,6 +373,7 @@ pub fn predicates_for_generics<'tcx>(cause: ObligationCause<'tcx>,
 /// conservative towards *no impl*, which is the opposite of the
 /// `evaluate` methods).
 pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                                param_env: ty::ParamEnv<'tcx>,
                                                 ty: Ty<'tcx>,
                                                 def_id: DefId,
                                                 span: Span)
@@ -385,6 +388,7 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
         substs: infcx.tcx.mk_substs_trait(ty, &[]),
     };
     let obligation = Obligation {
+        param_env,
         cause: ObligationCause::misc(span, ast::DUMMY_NODE_ID),
         recursion_depth: 0,
         predicate: trait_ref.to_predicate(),
@@ -408,7 +412,7 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
         // anyhow).
         let cause = ObligationCause::misc(span, ast::DUMMY_NODE_ID);
 
-        fulfill_cx.register_bound(infcx, ty, def_id, cause);
+        fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause);
 
         // Note: we only assume something is `Copy` if we can
         // *definitively* show that it implements `Copy`. Otherwise,
@@ -480,22 +484,24 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let elaborated_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
                                            unnormalized_env.reveal);
 
-    tcx.infer_ctxt(elaborated_env).enter(|infcx| {
+    tcx.infer_ctxt(()).enter(|infcx| {
         let predicates = match fully_normalize(
-                &infcx, cause,
-                // You would really want to pass infcx.param_env.caller_bounds here,
-                // but that is an interned slice, and fully_normalize takes &T and returns T, so
-                // without further refactoring, a slice can't be used. Luckily, we still have the
-                // predicate vector from which we created the ParamEnv in infcx, so we
-                // can pass that instead. It's roundabout and a bit brittle, but this code path
-                // ought to be refactored anyway, and until then it saves us from having to copy.
-                &predicates,
+            &infcx,
+            cause,
+            elaborated_env,
+            // You would really want to pass infcx.param_env.caller_bounds here,
+            // but that is an interned slice, and fully_normalize takes &T and returns T, so
+            // without further refactoring, a slice can't be used. Luckily, we still have the
+            // predicate vector from which we created the ParamEnv in infcx, so we
+            // can pass that instead. It's roundabout and a bit brittle, but this code path
+            // ought to be refactored anyway, and until then it saves us from having to copy.
+            &predicates,
         ) {
             Ok(predicates) => predicates,
             Err(errors) => {
                 infcx.report_fulfillment_errors(&errors);
                 // An unnormalized env is better than nothing.
-                return infcx.param_env;
+                return elaborated_env;
             }
         };
 
@@ -517,17 +523,17 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 // all things considered.
                 tcx.sess.span_err(span, &fixup_err.to_string());
                 // An unnormalized env is better than nothing.
-                return infcx.param_env;
+                return elaborated_env;
             }
         };
 
         let predicates = match tcx.lift_to_global(&predicates) {
             Some(predicates) => predicates,
-            None => return infcx.param_env
+            None => return elaborated_env,
         };
 
         debug!("normalize_param_env_or_error: resolved predicates={:?}",
-            predicates);
+               predicates);
 
         ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal)
     })
@@ -535,6 +541,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
 pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                           cause: ObligationCause<'tcx>,
+                                          param_env: ty::ParamEnv<'tcx>,
                                           value: &T)
                                           -> Result<T, Vec<FulfillmentError<'tcx>>>
     where T : TypeFoldable<'tcx>
@@ -558,7 +565,7 @@ pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     let mut fulfill_cx = FulfillmentContext::new();
 
     let Normalized { value: normalized_value, obligations } =
-        project::normalize(selcx, cause, value);
+        project::normalize(selcx, param_env, cause, value);
     debug!("fully_normalize: normalized_value={:?} obligations={:?}",
            normalized_value,
            obligations);
@@ -580,10 +587,10 @@ pub fn fully_normalize<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     Ok(resolved_value)
 }
 
-/// Normalizes the predicates and checks whether they hold.  If this
-/// returns false, then either normalize encountered an error or one
-/// of the predicates did not hold. Used when creating vtables to
-/// check for unsatisfiable methods.
+/// Normalizes the predicates and checks whether they hold in an empty
+/// environment. If this returns false, then either normalize
+/// encountered an error or one of the predicates did not hold. Used
+/// when creating vtables to check for unsatisfiable methods.
 pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                                predicates: Vec<ty::Predicate<'tcx>>)
                                                -> bool
@@ -591,17 +598,18 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     debug!("normalize_and_test_predicates(predicates={:?})",
            predicates);
 
-    tcx.infer_ctxt(Reveal::All).enter(|infcx| {
+    tcx.infer_ctxt(()).enter(|infcx| {
+        let param_env = ty::ParamEnv::empty(Reveal::All);
         let mut selcx = SelectionContext::new(&infcx);
         let mut fulfill_cx = FulfillmentContext::new();
         let cause = ObligationCause::dummy();
         let Normalized { value: predicates, obligations } =
-            normalize(&mut selcx, cause.clone(), &predicates);
+            normalize(&mut selcx, param_env, cause.clone(), &predicates);
         for obligation in obligations {
             fulfill_cx.register_predicate_obligation(&infcx, obligation);
         }
         for predicate in predicates {
-            let obligation = Obligation::new(cause.clone(), predicate);
+            let obligation = Obligation::new(cause.clone(), param_env, predicate);
             fulfill_cx.register_predicate_obligation(&infcx, obligation);
         }
 
@@ -663,30 +671,33 @@ pub fn get_vtable_methods<'a, 'tcx>(
 
 impl<'tcx,O> Obligation<'tcx,O> {
     pub fn new(cause: ObligationCause<'tcx>,
-               trait_ref: O)
+               param_env: ty::ParamEnv<'tcx>,
+               predicate: O)
                -> Obligation<'tcx, O>
     {
-        Obligation { cause: cause,
-                     recursion_depth: 0,
-                     predicate: trait_ref }
+        Obligation { cause, param_env, recursion_depth: 0, predicate }
     }
 
     fn with_depth(cause: ObligationCause<'tcx>,
                   recursion_depth: usize,
-                  trait_ref: O)
+                  param_env: ty::ParamEnv<'tcx>,
+                  predicate: O)
                   -> Obligation<'tcx, O>
     {
-        Obligation { cause: cause,
-                     recursion_depth: recursion_depth,
-                     predicate: trait_ref }
+        Obligation { cause, param_env, recursion_depth, predicate }
     }
 
-    pub fn misc(span: Span, body_id: ast::NodeId, trait_ref: O) -> Obligation<'tcx, O> {
-        Obligation::new(ObligationCause::misc(span, body_id), trait_ref)
+    pub fn misc(span: Span,
+                body_id: ast::NodeId,
+                param_env: ty::ParamEnv<'tcx>,
+                trait_ref: O)
+                -> Obligation<'tcx, O> {
+        Obligation::new(ObligationCause::misc(span, body_id), param_env, trait_ref)
     }
 
     pub fn with<P>(&self, value: P) -> Obligation<'tcx,P> {
         Obligation { cause: self.cause.clone(),
+                     param_env: self.param_env,
                      recursion_depth: self.recursion_depth,
                      predicate: value }
     }
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 467783fcd7d86..c155d1bf241b5 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -168,7 +168,8 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
 
     let Normalized { value: normalized_ty, mut obligations } =
         match opt_normalize_projection_type(selcx,
-                                            obligation.predicate.projection_ty.clone(),
+                                            obligation.param_env,
+                                            obligation.predicate.projection_ty,
                                             obligation.cause.clone(),
                                             obligation.recursion_depth) {
             Some(n) => n,
@@ -180,7 +181,11 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
            obligations);
 
     let infcx = selcx.infcx();
-    match infcx.eq_types(true, &obligation.cause, normalized_ty, obligation.predicate.ty) {
+    match infcx.eq_types(true,
+                         &obligation.cause,
+                         obligation.param_env,
+                         normalized_ty,
+                         obligation.predicate.ty) {
         Ok(InferOk { obligations: inferred_obligations, value: () }) => {
             obligations.extend(inferred_obligations);
             Ok(Some(obligations))
@@ -194,17 +199,19 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
 /// combines the normalized result and any additional obligations that
 /// were incurred as result.
 pub fn normalize<'a, 'b, 'gcx, 'tcx, T>(selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
+                                        param_env: ty::ParamEnv<'tcx>,
                                         cause: ObligationCause<'tcx>,
                                         value: &T)
                                         -> Normalized<'tcx, T>
     where T : TypeFoldable<'tcx>
 {
-    normalize_with_depth(selcx, cause, 0, value)
+    normalize_with_depth(selcx, param_env, cause, 0, value)
 }
 
 /// As `normalize`, but with a custom depth.
 pub fn normalize_with_depth<'a, 'b, 'gcx, 'tcx, T>(
     selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize,
     value: &T)
@@ -213,7 +220,7 @@ pub fn normalize_with_depth<'a, 'b, 'gcx, 'tcx, T>(
     where T : TypeFoldable<'tcx>
 {
     debug!("normalize_with_depth(depth={}, value={:?})", depth, value);
-    let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth);
+    let mut normalizer = AssociatedTypeNormalizer::new(selcx, param_env, cause, depth);
     let result = normalizer.fold(value);
     debug!("normalize_with_depth: depth={} result={:?} with {} obligations",
            depth, result, normalizer.obligations.len());
@@ -227,6 +234,7 @@ pub fn normalize_with_depth<'a, 'b, 'gcx, 'tcx, T>(
 
 struct AssociatedTypeNormalizer<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> {
     selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
     cause: ObligationCause<'tcx>,
     obligations: Vec<PredicateObligation<'tcx>>,
     depth: usize,
@@ -234,12 +242,14 @@ struct AssociatedTypeNormalizer<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> {
 
 impl<'a, 'b, 'gcx, 'tcx> AssociatedTypeNormalizer<'a, 'b, 'gcx, 'tcx> {
     fn new(selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
+           param_env: ty::ParamEnv<'tcx>,
            cause: ObligationCause<'tcx>,
            depth: usize)
            -> AssociatedTypeNormalizer<'a, 'b, 'gcx, 'tcx>
     {
         AssociatedTypeNormalizer {
             selcx: selcx,
+            param_env: param_env,
             cause: cause,
             obligations: vec![],
             depth: depth,
@@ -305,6 +315,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
 
                 let Normalized { value: normalized_ty, obligations } =
                     normalize_projection_type(self.selcx,
+                                              self.param_env,
                                               data.clone(),
                                               self.cause.clone(),
                                               self.depth);
@@ -344,12 +355,13 @@ impl<'tcx,T> Normalized<'tcx,T> {
 /// obligation `<T as Trait>::Item == $X` for later.
 pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
     selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
     projection_ty: ty::ProjectionTy<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize)
     -> NormalizedTy<'tcx>
 {
-    opt_normalize_projection_type(selcx, projection_ty.clone(), cause.clone(), depth)
+    opt_normalize_projection_type(selcx, param_env, projection_ty.clone(), cause.clone(), depth)
         .unwrap_or_else(move || {
             // if we bottom out in ambiguity, create a type variable
             // and a deferred predicate to resolve this when more type
@@ -366,7 +378,7 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
                 ty: ty_var
             });
             let obligation = Obligation::with_depth(
-                cause, depth + 1, projection.to_predicate());
+                cause, depth + 1, param_env, projection.to_predicate());
             Normalized {
                 value: ty_var,
                 obligations: vec![obligation]
@@ -380,6 +392,7 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
 /// which indicates that there are unbound type variables.
 fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
     selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
     projection_ty: ty::ProjectionTy<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize)
@@ -449,6 +462,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
             let recursion_limit = selcx.tcx().sess.recursion_limit.get();
             let obligation = Obligation::with_depth(cause.clone(),
                                                     recursion_limit,
+                                                    param_env,
                                                     projection_ty);
             selcx.infcx().report_overflow_error(&obligation, false);
         }
@@ -464,11 +478,11 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
         Err(ProjectionCacheEntry::Error) => {
             debug!("opt_normalize_projection_type: \
                     found error");
-            return Some(normalize_to_error(selcx, projection_ty, cause, depth));
+            return Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth));
         }
     }
 
-    let obligation = Obligation::with_depth(cause.clone(), depth, projection_ty.clone());
+    let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
     match project_type(selcx, &obligation) {
         Ok(ProjectedTy::Progress(Progress { ty: projected_ty,
                                             mut obligations,
@@ -489,7 +503,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
                    cacheable);
 
             let result = if projected_ty.has_projection_types() {
-                let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth+1);
+                let mut normalizer = AssociatedTypeNormalizer::new(selcx, param_env, cause, depth+1);
                 let normalized_ty = normalizer.fold(&projected_ty);
 
                 debug!("opt_normalize_projection_type: \
@@ -540,7 +554,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
 
             infcx.projection_cache.borrow_mut()
                                   .error(projection_ty);
-            Some(normalize_to_error(selcx, projection_ty, cause, depth))
+            Some(normalize_to_error(selcx, param_env, projection_ty, cause, depth))
         }
     }
 }
@@ -565,6 +579,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
 /// because it contains `[type error]`. Yuck! (See issue #29857 for
 /// one case where this arose.)
 fn normalize_to_error<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tcx>,
+                                      param_env: ty::ParamEnv<'tcx>,
                                       projection_ty: ty::ProjectionTy<'tcx>,
                                       cause: ObligationCause<'tcx>,
                                       depth: usize)
@@ -573,6 +588,7 @@ fn normalize_to_error<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tc
     let trait_ref = projection_ty.trait_ref.to_poly_trait_ref();
     let trait_obligation = Obligation { cause: cause,
                                         recursion_depth: depth,
+                                        param_env,
                                         predicate: trait_ref.to_predicate() };
     let tcx = selcx.infcx().tcx;
     let def_id = tcx.associated_items(projection_ty.trait_ref.def_id).find(|i|
@@ -746,13 +762,12 @@ fn assemble_candidates_from_param_env<'cx, 'gcx, 'tcx>(
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
 {
     debug!("assemble_candidates_from_param_env(..)");
-    let env_predicates = selcx.param_env().caller_bounds.iter().cloned();
     assemble_candidates_from_predicates(selcx,
                                         obligation,
                                         obligation_trait_ref,
                                         candidate_set,
                                         ProjectionTyCandidate::ParamEnv,
-                                        env_predicates);
+                                        obligation.param_env.caller_bounds.iter().cloned());
 }
 
 /// In the case of a nested projection like <<A as Foo>::FooT as Bar>::BarT, we may find
@@ -807,7 +822,7 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
     ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>,
     env_predicates: I)
-    where I: Iterator<Item=ty::Predicate<'tcx>>
+    where I: IntoIterator<Item=ty::Predicate<'tcx>>
 {
     debug!("assemble_candidates_from_predicates(obligation={:?})",
            obligation);
@@ -827,6 +842,7 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
                         obligation_trait_ref.to_poly_trait_ref();
                     infcx.sub_poly_trait_refs(false,
                                               obligation.cause.clone(),
+                                              obligation.param_env,
                                               data_poly_trait_ref,
                                               obligation_poly_trait_ref)
                         .map(|InferOk { obligations: _, value: () }| {
@@ -936,7 +952,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
                 // get a result which isn't correct for all monomorphizations.
                 let new_candidate = if !is_default {
                     Some(ProjectionTyCandidate::Select)
-                } else if selcx.projection_mode() == Reveal::All {
+                } else if obligation.param_env.reveal == Reveal::All {
                     assert!(!poly_trait_ref.needs_infer());
                     if !poly_trait_ref.needs_subst() {
                         Some(ProjectionTyCandidate::Select)
@@ -1096,6 +1112,7 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
             selcx.infcx().probe(|_| {
                 selcx.infcx().sub_poly_trait_refs(false,
                                                   obligation.cause.clone(),
+                                                  obligation.param_env,
                                                   data_poly_trait_ref,
                                                   obligation_poly_trait_ref).is_ok()
             })
@@ -1143,6 +1160,7 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>(
         value: closure_type,
         obligations
     } = normalize_with_depth(selcx,
+                             obligation.param_env,
                              obligation.cause.clone(),
                              obligation.recursion_depth+1,
                              &closure_type);
@@ -1203,8 +1221,9 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
 {
     let infcx = selcx.infcx();
     let cause = obligation.cause.clone();
+    let param_env = obligation.param_env;
     let trait_ref = obligation.predicate.trait_ref;
-    match infcx.match_poly_projection_predicate(cause, poly_projection, trait_ref) {
+    match infcx.match_poly_projection_predicate(cause, param_env, poly_projection, trait_ref) {
         Ok(InferOk { value: ty_match, obligations }) => {
             Progress {
                 ty: ty_match.value,
@@ -1233,6 +1252,7 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
     let VtableImplData { substs, nested, impl_def_id } = impl_vtable;
 
     let tcx = selcx.tcx();
+    let param_env = obligation.param_env;
     let assoc_ty = assoc_ty_def(selcx, impl_def_id, obligation.predicate.item_name(tcx));
 
     let ty = if !assoc_ty.item.defaultness.has_value() {
@@ -1247,7 +1267,7 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
     } else {
         tcx.type_of(assoc_ty.item.def_id)
     };
-    let substs = translate_substs(selcx.infcx(), impl_def_id, substs, assoc_ty.node);
+    let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.node);
     Progress {
         ty: ty.subst(tcx, substs),
         obligations: nested,
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 12f9e2f355bb9..21d4df3d0990d 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -21,7 +21,6 @@ use super::{PredicateObligation, TraitObligation, ObligationCause};
 use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
 use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
 use super::{ObjectCastObligation, Obligation};
-use super::Reveal;
 use super::TraitNotObjectSafe;
 use super::Selection;
 use super::SelectionResult;
@@ -315,18 +314,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         self.infcx.tcx
     }
 
-    pub fn param_env(&self) -> ty::ParamEnv<'gcx> {
-        self.infcx.param_env()
-    }
-
     pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'gcx, 'tcx> {
         self.infcx
     }
 
-    pub fn projection_mode(&self) -> Reveal {
-        self.param_env().reveal
-    }
-
     /// Wraps the inference context's in_snapshot s.t. snapshot handling is only from the selection
     /// context's self.
     fn in_snapshot<R, F>(&mut self, f: F) -> R
@@ -540,7 +531,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
             ty::Predicate::Equate(ref p) => {
                 // does this code ever run?
-                match self.infcx.equality_predicate(&obligation.cause, p) {
+                match self.infcx.equality_predicate(&obligation.cause, obligation.param_env, p) {
                     Ok(InferOk { obligations, .. }) => {
                         self.inferred_obligations.extend(obligations);
                         EvaluatedToOk
@@ -551,7 +542,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
             ty::Predicate::Subtype(ref p) => {
                 // does this code ever run?
-                match self.infcx.subtype_predicate(&obligation.cause, p) {
+                match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) {
                     Some(Ok(InferOk { obligations, .. })) => {
                         self.inferred_obligations.extend(obligations);
                         EvaluatedToOk
@@ -562,7 +553,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             }
 
             ty::Predicate::WellFormed(ty) => {
-                match ty::wf::obligations(self.infcx, obligation.cause.body_id,
+                match ty::wf::obligations(self.infcx, obligation.param_env, obligation.cause.body_id,
                                           ty, obligation.cause.span) {
                     Some(obligations) =>
                         self.evaluate_predicates_recursively(previous_stack, obligations.iter()),
@@ -628,7 +619,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let stack = self.push_stack(previous_stack, obligation);
         let fresh_trait_ref = stack.fresh_trait_ref;
-        if let Some(result) = self.check_evaluation_cache(fresh_trait_ref) {
+        if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) {
             debug!("CACHE HIT: EVAL({:?})={:?}",
                    fresh_trait_ref,
                    result);
@@ -640,7 +631,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         debug!("CACHE MISS: EVAL({:?})={:?}",
                fresh_trait_ref,
                result);
-        self.insert_evaluation_cache(fresh_trait_ref, result);
+        self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, result);
 
         result
     }
@@ -751,10 +742,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         result
     }
 
-    fn check_evaluation_cache(&self, trait_ref: ty::PolyTraitRef<'tcx>)
+    fn check_evaluation_cache(&self,
+                              param_env: ty::ParamEnv<'tcx>,
+                              trait_ref: ty::PolyTraitRef<'tcx>)
                               -> Option<EvaluationResult>
     {
-        if self.can_use_global_caches() {
+        if self.can_use_global_caches(param_env) {
             let cache = self.tcx().evaluation_cache.hashmap.borrow();
             if let Some(cached) = cache.get(&trait_ref) {
                 return Some(cached.clone());
@@ -764,6 +757,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn insert_evaluation_cache(&mut self,
+                               param_env: ty::ParamEnv<'tcx>,
                                trait_ref: ty::PolyTraitRef<'tcx>,
                                result: EvaluationResult)
     {
@@ -778,7 +772,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             return;
         }
 
-        if self.can_use_global_caches() {
+        if self.can_use_global_caches(param_env) {
             let mut cache = self.tcx().evaluation_cache.hashmap.borrow_mut();
             if let Some(trait_ref) = self.tcx().lift_to_global(&trait_ref) {
                 cache.insert(trait_ref, result);
@@ -819,7 +813,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                stack);
         assert!(!stack.obligation.predicate.has_escaping_regions());
 
-        if let Some(c) = self.check_candidate_cache(&cache_fresh_trait_pred) {
+        if let Some(c) = self.check_candidate_cache(stack.obligation.param_env,
+                                                    &cache_fresh_trait_pred) {
             debug!("CACHE HIT: SELECT({:?})={:?}",
                    cache_fresh_trait_pred,
                    c);
@@ -832,7 +827,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         if self.should_update_candidate_cache(&cache_fresh_trait_pred, &candidate) {
             debug!("CACHE MISS: SELECT({:?})={:?}",
                    cache_fresh_trait_pred, candidate);
-            self.insert_candidate_cache(cache_fresh_trait_pred, candidate.clone());
+            self.insert_candidate_cache(stack.obligation.param_env,
+                                        cache_fresh_trait_pred,
+                                        candidate.clone());
         }
 
         candidate
@@ -995,7 +992,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// Returns true if the global caches can be used.
     /// Do note that if the type itself is not in the
     /// global tcx, the local caches will be used.
-    fn can_use_global_caches(&self) -> bool {
+    fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool {
         // If there are any where-clauses in scope, then we always use
         // a cache local to this particular scope. Otherwise, we
         // switch to a global cache. We used to try and draw
@@ -1003,7 +1000,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // annoying and weird bugs like #22019 and #18290. This simple
         // rule seems to be pretty clearly safe and also still retains
         // a very high hit rate (~95% when compiling rustc).
-        if !self.param_env().caller_bounds.is_empty() {
+        if !param_env.caller_bounds.is_empty() {
             return false;
         }
 
@@ -1023,11 +1020,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn check_candidate_cache(&mut self,
+                             param_env: ty::ParamEnv<'tcx>,
                              cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>)
                              -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>>
     {
         let trait_ref = &cache_fresh_trait_pred.0.trait_ref;
-        if self.can_use_global_caches() {
+        if self.can_use_global_caches(param_env) {
             let cache = self.tcx().selection_cache.hashmap.borrow();
             if let Some(cached) = cache.get(&trait_ref) {
                 return Some(cached.clone());
@@ -1037,11 +1035,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn insert_candidate_cache(&mut self,
+                              param_env: ty::ParamEnv<'tcx>,
                               cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
                               candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>)
     {
         let trait_ref = cache_fresh_trait_pred.0.trait_ref;
-        if self.can_use_global_caches() {
+        if self.can_use_global_caches(param_env) {
             let mut cache = self.tcx().selection_cache.hashmap.borrow_mut();
             if let Some(trait_ref) = self.tcx().lift_to_global(&trait_ref) {
                 if let Some(candidate) = self.tcx().lift_to_global(&candidate) {
@@ -1099,6 +1098,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     {
         let TraitObligationStack { obligation, .. } = *stack;
         let ref obligation = Obligation {
+            param_env: obligation.param_env,
             cause: obligation.cause.clone(),
             recursion_depth: obligation.recursion_depth,
             predicate: self.infcx().resolve_type_vars_if_possible(&obligation.predicate)
@@ -1272,6 +1272,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let cause = obligation.cause.clone();
         match self.infcx.sub_poly_trait_refs(false,
                                              cause,
+                                             obligation.param_env,
                                              trait_bound.clone(),
                                              ty::Binder(skol_trait_ref.clone())) {
             Ok(InferOk { obligations, .. }) => {
@@ -1296,9 +1297,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                stack.obligation);
 
         let all_bounds =
-            self.param_env().caller_bounds
-                            .iter()
-                            .filter_map(|o| o.to_opt_poly_trait_ref());
+            stack.obligation.param_env.caller_bounds
+                                      .iter()
+                                      .filter_map(|o| o.to_opt_poly_trait_ref());
 
         // micro-optimization: filter out predicates relating to different
         // traits.
@@ -1953,6 +1954,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn collect_predicates_for_types(&mut self,
+                                    param_env: ty::ParamEnv<'tcx>,
                                     cause: ObligationCause<'tcx>,
                                     recursion_depth: usize,
                                     trait_def_id: DefId,
@@ -1981,16 +1983,17 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     this.infcx().skolemize_late_bound_regions(&ty, snapshot);
                 let Normalized { value: normalized_ty, mut obligations } =
                     project::normalize_with_depth(this,
+                                                  param_env,
                                                   cause.clone(),
                                                   recursion_depth,
                                                   &skol_ty);
                 let skol_obligation =
-                    this.tcx().predicate_for_trait_def(
-                                                  cause.clone(),
-                                                  trait_def_id,
-                                                  recursion_depth,
-                                                  normalized_ty,
-                                                  &[]);
+                    this.tcx().predicate_for_trait_def(param_env,
+                                                       cause.clone(),
+                                                       trait_def_id,
+                                                       recursion_depth,
+                                                       normalized_ty,
+                                                       &[]);
                 obligations.push(skol_obligation);
                 this.infcx().plug_leaks(skol_map, snapshot, obligations)
             })
@@ -2131,7 +2134,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             };
 
             let cause = obligation.derived_cause(BuiltinDerivedObligation);
-            self.collect_predicates_for_types(cause,
+            self.collect_predicates_for_types(obligation.param_env,
+                                              cause,
                                               obligation.recursion_depth+1,
                                               trait_def,
                                               nested)
@@ -2175,6 +2179,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let cause = obligation.derived_cause(BuiltinDerivedObligation);
         let mut obligations = self.collect_predicates_for_types(
+            obligation.param_env,
             cause,
             obligation.recursion_depth+1,
             trait_def_id,
@@ -2187,6 +2192,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             let cause = obligation.derived_cause(ImplDerivedObligation);
             this.impl_or_trait_obligations(cause,
                                            obligation.recursion_depth + 1,
+                                           obligation.param_env,
                                            trait_def_id,
                                            &trait_ref.substs,
                                            skol_map,
@@ -2220,9 +2226,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                   snapshot);
             debug!("confirm_impl_candidate substs={:?}", substs);
             let cause = obligation.derived_cause(ImplDerivedObligation);
-            this.vtable_impl(impl_def_id, substs, cause,
+            this.vtable_impl(impl_def_id,
+                             substs,
+                             cause,
                              obligation.recursion_depth + 1,
-                             skol_map, snapshot)
+                             obligation.param_env,
+                             skol_map,
+                             snapshot)
         })
     }
 
@@ -2231,6 +2241,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                    mut substs: Normalized<'tcx, &'tcx Substs<'tcx>>,
                    cause: ObligationCause<'tcx>,
                    recursion_depth: usize,
+                   param_env: ty::ParamEnv<'tcx>,
                    skol_map: infer::SkolemizationMap<'tcx>,
                    snapshot: &infer::CombinedSnapshot)
                    -> VtableImplData<'tcx, PredicateObligation<'tcx>>
@@ -2244,6 +2255,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let mut impl_obligations =
             self.impl_or_trait_obligations(cause,
                                            recursion_depth,
+                                           param_env,
                                            impl_def_id,
                                            &substs.value,
                                            skol_map,
@@ -2345,6 +2357,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             .map_bound(|(trait_ref, _)| trait_ref);
 
         self.confirm_poly_trait_refs(obligation.cause.clone(),
+                                     obligation.param_env,
                                      obligation.predicate.to_poly_trait_ref(),
                                      trait_ref)?;
         Ok(VtableFnPointerData { fn_ty: self_ty, nested: vec![] })
@@ -2374,12 +2387,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                obligations);
 
         self.confirm_poly_trait_refs(obligation.cause.clone(),
+                                     obligation.param_env,
                                      obligation.predicate.to_poly_trait_ref(),
                                      trait_ref)?;
 
         obligations.push(Obligation::new(
-                obligation.cause.clone(),
-                ty::Predicate::ClosureKind(closure_def_id, kind)));
+            obligation.cause.clone(),
+            obligation.param_env,
+            ty::Predicate::ClosureKind(closure_def_id, kind)));
 
         Ok(VtableClosureData {
             closure_def_id: closure_def_id,
@@ -2415,6 +2430,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// report an error to the user.
     fn confirm_poly_trait_refs(&mut self,
                                obligation_cause: ObligationCause<'tcx>,
+                               obligation_param_env: ty::ParamEnv<'tcx>,
                                obligation_trait_ref: ty::PolyTraitRef<'tcx>,
                                expected_trait_ref: ty::PolyTraitRef<'tcx>)
                                -> Result<(), SelectionError<'tcx>>
@@ -2422,6 +2438,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let obligation_trait_ref = obligation_trait_ref.clone();
         self.infcx.sub_poly_trait_refs(false,
                                        obligation_cause.clone(),
+                                       obligation_param_env,
                                        expected_trait_ref.clone(),
                                        obligation_trait_ref.clone())
             .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
@@ -2458,7 +2475,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 let new_trait = tcx.mk_dynamic(
                     ty::Binder(tcx.mk_existential_predicates(iter)), r_b);
                 let InferOk { obligations, .. } =
-                    self.infcx.eq_types(false, &obligation.cause, new_trait, target)
+                    self.infcx.eq_types(false,
+                                        &obligation.cause,
+                                        obligation.param_env,
+                                        new_trait,
+                                        target)
                     .map_err(|_| Unimplemented)?;
                 self.inferred_obligations.extend(obligations);
 
@@ -2469,6 +2490,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 let outlives = ty::OutlivesPredicate(r_a, r_b);
                 nested.push(Obligation::with_depth(cause,
                                                    obligation.recursion_depth + 1,
+                                                   obligation.param_env,
                                                    ty::Binder(outlives).to_predicate()));
             }
 
@@ -2488,6 +2510,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 let mut push = |predicate| {
                     nested.push(Obligation::with_depth(cause.clone(),
                                                        obligation.recursion_depth + 1,
+                                                       obligation.param_env,
                                                        predicate));
                 };
 
@@ -2517,7 +2540,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // [T; n] -> [T].
             (&ty::TyArray(a, _), &ty::TySlice(b)) => {
                 let InferOk { obligations, .. } =
-                    self.infcx.eq_types(false, &obligation.cause, a, b)
+                    self.infcx.eq_types(false, &obligation.cause, obligation.param_env, a, b)
                     .map_err(|_| Unimplemented)?;
                 self.inferred_obligations.extend(obligations);
             }
@@ -2580,12 +2603,17 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 });
                 let new_struct = tcx.mk_adt(def, tcx.mk_substs(params));
                 let InferOk { obligations, .. } =
-                    self.infcx.eq_types(false, &obligation.cause, new_struct, target)
+                    self.infcx.eq_types(false,
+                                        &obligation.cause,
+                                        obligation.param_env,
+                                        new_struct,
+                                        target)
                     .map_err(|_| Unimplemented)?;
                 self.inferred_obligations.extend(obligations);
 
                 // Construct the nested Field<T>: Unsize<Field<U>> predicate.
                 nested.push(tcx.predicate_for_trait_def(
+                    obligation.param_env,
                     obligation.cause.clone(),
                     obligation.predicate.def_id(),
                     obligation.recursion_depth + 1,
@@ -2655,6 +2683,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let impl_trait_ref =
             project::normalize_with_depth(self,
+                                          obligation.param_env,
                                           obligation.cause.clone(),
                                           obligation.recursion_depth + 1,
                                           &impl_trait_ref);
@@ -2669,6 +2698,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let InferOk { obligations, .. } =
             self.infcx.eq_trait_refs(false,
                                      &obligation.cause,
+                                     obligation.param_env,
                                      impl_trait_ref.value.clone(),
                                      skol_obligation_trait_ref)
             .map_err(|e| {
@@ -2742,6 +2772,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         self.infcx.sub_poly_trait_refs(false,
                                        obligation.cause.clone(),
+                                       obligation.param_env,
                                        poly_trait_ref,
                                        obligation.predicate.to_poly_trait_ref())
             .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
@@ -2809,6 +2840,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // A closure signature can contain associated types which
         // must be normalized.
         normalize_with_depth(self,
+                             obligation.param_env,
                              obligation.cause.clone(),
                              obligation.recursion_depth+1,
                              &trait_ref)
@@ -2821,6 +2853,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn impl_or_trait_obligations(&mut self,
                                  cause: ObligationCause<'tcx>,
                                  recursion_depth: usize,
+                                 param_env: ty::ParamEnv<'tcx>,
                                  def_id: DefId, // of impl or trait
                                  substs: &Substs<'tcx>, // for impl or trait
                                  skol_map: infer::SkolemizationMap<'tcx>,
@@ -2847,12 +2880,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let predicates = tcx.predicates_of(def_id);
         assert_eq!(predicates.parent, None);
         let predicates = predicates.predicates.iter().flat_map(|predicate| {
-            let predicate = normalize_with_depth(self, cause.clone(), recursion_depth,
+            let predicate = normalize_with_depth(self, param_env, cause.clone(), recursion_depth,
                                                  &predicate.subst(tcx, substs));
             predicate.obligations.into_iter().chain(
                 Some(Obligation {
                     cause: cause.clone(),
                     recursion_depth: recursion_depth,
+                    param_env,
                     predicate: predicate.value
                 }))
         }).collect();
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index 4d7fdbd881ec3..0e9bf93cbd2d1 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -41,6 +41,7 @@ pub struct OverlapError {
 /// Given a subst for the requested impl, translate it to a subst
 /// appropriate for the actual item definition (whether it be in that impl,
 /// a parent impl, or the trait).
+///
 /// When we have selected one impl, but are actually using item definitions from
 /// a parent impl providing a default, we need a way to translate between the
 /// type parameters of the two impls. Here the `source_impl` is the one we've
@@ -73,6 +74,7 @@ pub struct OverlapError {
 /// *fulfillment* to relate the two impls, requiring that all projections are
 /// resolved.
 pub fn translate_substs<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                        param_env: ty::ParamEnv<'tcx>,
                                         source_impl: DefId,
                                         source_substs: &'tcx Substs<'tcx>,
                                         target_node: specialization_graph::Node)
@@ -91,10 +93,11 @@ pub fn translate_substs<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                 return source_substs;
             }
 
-            fulfill_implication(infcx, source_trait_ref, target_impl).unwrap_or_else(|_| {
-                bug!("When translating substitutions for specialization, the expected \
-                      specializaiton failed to hold")
-            })
+            fulfill_implication(infcx, param_env, source_trait_ref, target_impl)
+                .unwrap_or_else(|_| {
+                    bug!("When translating substitutions for specialization, the expected \
+                          specializaiton failed to hold")
+                })
         }
         specialization_graph::Node::Trait(..) => source_trait_ref.substs,
     };
@@ -122,9 +125,10 @@ pub fn find_associated_item<'a, 'tcx>(
     let ancestors = trait_def.ancestors(tcx, impl_data.impl_def_id);
     match ancestors.defs(tcx, item.name, item.kind).next() {
         Some(node_item) => {
-            let substs = tcx.infer_ctxt(Reveal::All).enter(|infcx| {
+            let substs = tcx.infer_ctxt(()).enter(|infcx| {
+                let param_env = ty::ParamEnv::empty(Reveal::All);
                 let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
-                let substs = translate_substs(&infcx, impl_data.impl_def_id,
+                let substs = translate_substs(&infcx, param_env, impl_data.impl_def_id,
                                               substs, node_item.node);
                 let substs = infcx.tcx.erase_regions(&substs);
                 tcx.lift(&substs).unwrap_or_else(|| {
@@ -184,11 +188,14 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
 
     // Create a infcx, taking the predicates of impl1 as assumptions:
-    let result = tcx.infer_ctxt(penv).enter(|infcx| {
+    let result = tcx.infer_ctxt(()).enter(|infcx| {
         // Normalize the trait reference. The WF rules ought to ensure
         // that this always succeeds.
         let impl1_trait_ref =
-            match traits::fully_normalize(&infcx, ObligationCause::dummy(), &impl1_trait_ref) {
+            match traits::fully_normalize(&infcx,
+                                          ObligationCause::dummy(),
+                                          penv,
+                                          &impl1_trait_ref) {
                 Ok(impl1_trait_ref) => impl1_trait_ref,
                 Err(err) => {
                     bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
@@ -196,7 +203,7 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             };
 
         // Attempt to prove that impl2 applies, given all of the above.
-        fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok()
+        fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok()
     });
 
     tcx.specializes_cache.borrow_mut().insert(impl1_def_id, impl2_def_id, result);
@@ -209,18 +216,21 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 /// `source_trait_ref` and those whose identity is determined via a where
 /// clause in the impl.
 fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                       param_env: ty::ParamEnv<'tcx>,
                                        source_trait_ref: ty::TraitRef<'tcx>,
                                        target_impl: DefId)
                                        -> Result<&'tcx Substs<'tcx>, ()> {
     let selcx = &mut SelectionContext::new(&infcx);
     let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl);
     let (target_trait_ref, mut obligations) = impl_trait_ref_and_oblig(selcx,
-                                                                   target_impl,
-                                                                   target_substs);
+                                                                       param_env,
+                                                                       target_impl,
+                                                                       target_substs);
 
     // do the impls unify? If not, no specialization.
     match infcx.eq_trait_refs(true,
                               &ObligationCause::dummy(),
+                              param_env,
                               source_trait_ref,
                               target_trait_ref) {
         Ok(InferOk { obligations: o, .. }) => {
@@ -250,7 +260,7 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                        source_trait_ref,
                        target_trait_ref,
                        errors,
-                       infcx.param_env.caller_bounds);
+                       param_env.caller_bounds);
                 Err(())
             }
 
diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs
index aa35dfd1d70ad..702c5035a18b7 100644
--- a/src/librustc/traits/specialize/specialization_graph.rs
+++ b/src/librustc/traits/specialize/specialization_graph.rs
@@ -11,7 +11,7 @@
 use super::{OverlapError, specializes};
 
 use hir::def_id::DefId;
-use traits::{self, Reveal};
+use traits;
 use ty::{self, TyCtxt, TypeFoldable};
 use ty::fast_reject::{self, SimplifiedType};
 use std::rc::Rc;
@@ -109,7 +109,7 @@ impl<'a, 'gcx, 'tcx> Children {
             let possible_sibling = *slot;
 
             let tcx = tcx.global_tcx();
-            let (le, ge) = tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
+            let (le, ge) = tcx.infer_ctxt(()).enter(|infcx| {
                 let overlap = traits::overlapping_impls(&infcx,
                                                         possible_sibling,
                                                         impl_def_id);
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index 9d0b1035ade49..4abb0cb549db4 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -341,6 +341,7 @@ impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx
             cause: self.cause.clone(),
             recursion_depth: self.recursion_depth,
             predicate: self.predicate.fold_with(folder),
+            param_env: self.param_env.fold_with(folder),
         }
     }
 
diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs
index 4cffe6af083b6..7ad2ef90f0d49 100644
--- a/src/librustc/traits/trans/mod.rs
+++ b/src/librustc/traits/trans/mod.rs
@@ -46,12 +46,14 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
 
             // Do the initial selection for the obligation. This yields the
             // shallow result we are looking for -- that is, what specific impl.
-            self.infer_ctxt(Reveal::All).enter(|infcx| {
+            self.infer_ctxt(()).enter(|infcx| {
                 let mut selcx = SelectionContext::new(&infcx);
 
+                let param_env = ty::ParamEnv::empty(Reveal::All);
                 let obligation_cause = ObligationCause::misc(span,
                                                              ast::DUMMY_NODE_ID);
                 let obligation = Obligation::new(obligation_cause,
+                                                 param_env,
                                                  trait_ref.to_poly_trait_predicate());
 
                 let selection = match selcx.select(&obligation) {
diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs
index 3f5cf7eca5307..c385927811cf7 100644
--- a/src/librustc/traits/util.rs
+++ b/src/librustc/traits/util.rs
@@ -358,6 +358,7 @@ impl<'tcx,I:Iterator<Item=ty::Predicate<'tcx>>> Iterator for FilterToTraits<I> {
 /// returning the resulting trait ref and all obligations that arise.
 /// The obligations are closed under normalization.
 pub fn impl_trait_ref_and_oblig<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tcx>,
+                                                param_env: ty::ParamEnv<'tcx>,
                                                 impl_def_id: DefId,
                                                 impl_substs: &Substs<'tcx>)
                                                 -> (ty::TraitRef<'tcx>,
@@ -368,14 +369,14 @@ pub fn impl_trait_ref_and_oblig<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a,
     let impl_trait_ref =
         impl_trait_ref.subst(selcx.tcx(), impl_substs);
     let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
-        super::normalize(selcx, ObligationCause::dummy(), &impl_trait_ref);
+        super::normalize(selcx, param_env, ObligationCause::dummy(), &impl_trait_ref);
 
     let predicates = selcx.tcx().predicates_of(impl_def_id);
     let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
     let Normalized { value: predicates, obligations: normalization_obligations2 } =
-        super::normalize(selcx, ObligationCause::dummy(), &predicates);
+        super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates);
     let impl_obligations =
-        predicates_for_generics(ObligationCause::dummy(), 0, &predicates);
+        predicates_for_generics(ObligationCause::dummy(), 0, param_env, &predicates);
 
     let impl_obligations: Vec<_> =
         impl_obligations.into_iter()
@@ -389,6 +390,7 @@ pub fn impl_trait_ref_and_oblig<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a,
 /// See `super::obligations_for_generics`
 pub fn predicates_for_generics<'tcx>(cause: ObligationCause<'tcx>,
                                      recursion_depth: usize,
+                                     param_env: ty::ParamEnv<'tcx>,
                                      generic_bounds: &ty::InstantiatedPredicates<'tcx>)
                                      -> Vec<PredicateObligation<'tcx>>
 {
@@ -398,18 +400,21 @@ pub fn predicates_for_generics<'tcx>(cause: ObligationCause<'tcx>,
     generic_bounds.predicates.iter().map(|predicate| {
         Obligation { cause: cause.clone(),
                      recursion_depth: recursion_depth,
+                     param_env: param_env,
                      predicate: predicate.clone() }
     }).collect()
 }
 
 pub fn predicate_for_trait_ref<'tcx>(
     cause: ObligationCause<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
     recursion_depth: usize)
     -> PredicateObligation<'tcx>
 {
     Obligation {
         cause: cause,
+        param_env: param_env,
         recursion_depth: recursion_depth,
         predicate: trait_ref.to_predicate(),
     }
@@ -417,18 +422,19 @@ pub fn predicate_for_trait_ref<'tcx>(
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn predicate_for_trait_def(self,
-        cause: ObligationCause<'tcx>,
-        trait_def_id: DefId,
-        recursion_depth: usize,
-        param_ty: Ty<'tcx>,
-        ty_params: &[Ty<'tcx>])
+                                   param_env: ty::ParamEnv<'tcx>,
+                                   cause: ObligationCause<'tcx>,
+                                   trait_def_id: DefId,
+                                   recursion_depth: usize,
+                                   param_ty: Ty<'tcx>,
+                                   ty_params: &[Ty<'tcx>])
         -> PredicateObligation<'tcx>
     {
         let trait_ref = ty::TraitRef {
             def_id: trait_def_id,
             substs: self.mk_substs_trait(param_ty, ty_params)
         };
-        predicate_for_trait_ref(cause, trait_ref, recursion_depth)
+        predicate_for_trait_ref(cause, param_env, trait_ref, recursion_depth)
     }
 
     /// Cast a trait reference into a reference to one of its super
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index e0e195867131f..87cee2ddc03f8 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -765,6 +765,18 @@ pub trait Lift<'tcx> {
     fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted>;
 }
 
+impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> {
+    type Lifted = ty::ParamEnv<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<ty::ParamEnv<'tcx>> {
+        self.caller_bounds.lift_to_tcx(tcx).and_then(|caller_bounds| {
+            Some(ty::ParamEnv {
+                reveal: self.reveal,
+                caller_bounds,
+            })
+        })
+    }
+}
+
 impl<'a, 'tcx> Lift<'tcx> for Ty<'a> {
     type Lifted = Ty<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Ty<'tcx>> {
@@ -851,6 +863,25 @@ impl<'a, 'tcx> Lift<'tcx> for &'a Slice<ExistentialPredicate<'a>> {
     }
 }
 
+impl<'a, 'tcx> Lift<'tcx> for &'a Slice<Predicate<'a>> {
+    type Lifted = &'tcx Slice<Predicate<'tcx>>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>)
+        -> Option<&'tcx Slice<Predicate<'tcx>>> {
+        if self.is_empty() {
+            return Some(Slice::empty());
+        }
+        if tcx.interners.arena.in_arena(*self as *const _) {
+            return Some(unsafe { mem::transmute(*self) });
+        }
+        // Also try in the global tcx if we're not that.
+        if !tcx.is_global() {
+            self.lift_to_tcx(tcx.global_tcx())
+        } else {
+            None
+        }
+    }
+}
+
 pub mod tls {
     use super::{CtxtInterners, GlobalCtxt, TyCtxt};
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 0a25cd638cc30..8c991be0d12d4 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -158,29 +158,6 @@ pub struct ImplHeader<'tcx> {
     pub predicates: Vec<Predicate<'tcx>>,
 }
 
-impl<'a, 'gcx, 'tcx> ImplHeader<'tcx> {
-    pub fn with_fresh_ty_vars(selcx: &mut traits::SelectionContext<'a, 'gcx, 'tcx>,
-                              impl_def_id: DefId)
-                              -> ImplHeader<'tcx>
-    {
-        let tcx = selcx.tcx();
-        let impl_substs = selcx.infcx().fresh_substs_for_item(DUMMY_SP, impl_def_id);
-
-        let header = ImplHeader {
-            impl_def_id: impl_def_id,
-            self_ty: tcx.type_of(impl_def_id),
-            trait_ref: tcx.impl_trait_ref(impl_def_id),
-            predicates: tcx.predicates_of(impl_def_id).predicates
-        }.subst(tcx, impl_substs);
-
-        let traits::Normalized { value: mut header, obligations } =
-            traits::normalize(selcx, traits::ObligationCause::dummy(), &header);
-
-        header.predicates.extend(obligations.into_iter().map(|o| o.predicate));
-        header
-    }
-}
-
 #[derive(Copy, Clone, Debug)]
 pub struct AssociatedItem {
     pub def_id: DefId,
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 3a6147f911cce..1e2689243903e 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -466,6 +466,20 @@ impl<'tcx, T:TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<T> {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for ty::ParamEnv<'tcx> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        ty::ParamEnv {
+            reveal: self.reveal,
+            caller_bounds: self.caller_bounds.fold_with(folder),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        let &ty::ParamEnv { reveal: _, ref caller_bounds } = self;
+        caller_bounds.super_visit_with(visitor)
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice<ty::ExistentialPredicate<'tcx>> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         let v = self.iter().map(|p| p.fold_with(folder)).collect::<AccumulateVec<[_; 8]>>();
@@ -771,6 +785,17 @@ impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice<ty::Predicate<'tcx>> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        let v = self.iter().map(|p| p.fold_with(folder)).collect::<AccumulateVec<[_; 8]>>();
+        folder.tcx().intern_predicates(&v)
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.iter().any(|p| p.visit_with(visitor))
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         match *self {
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 6d55f04e86aaa..8c2577a382431 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -170,11 +170,12 @@ impl<'tcx> ty::ParamEnv<'tcx> {
         ty::ParamEnv { reveal: Reveal::All, ..self }
     }
 
-    pub fn can_type_implement_copy<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    pub fn can_type_implement_copy<'a>(self,
+                                       tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                        self_type: Ty<'tcx>, span: Span)
-                                       -> Result<(), CopyImplementationError> {
+                                       -> Result<(), CopyImplementationError<'tcx>> {
         // FIXME: (@jroesch) float this code up
-        tcx.infer_ctxt(self.clone()).enter(|infcx| {
+        tcx.infer_ctxt(()).enter(|infcx| {
             let (adt, substs) = match self_type.sty {
                 ty::TyAdt(adt, substs) => (adt, substs),
                 _ => return Err(CopyImplementationError::NotAnAdt),
@@ -182,8 +183,8 @@ impl<'tcx> ty::ParamEnv<'tcx> {
 
             let field_implements_copy = |field: &ty::FieldDef| {
                 let cause = traits::ObligationCause::dummy();
-                match traits::fully_normalize(&infcx, cause, &field.ty(tcx, substs)) {
-                    Ok(ty) => !infcx.type_moves_by_default(ty, span),
+                match traits::fully_normalize(&infcx, cause, self, &field.ty(tcx, substs)) {
+                    Ok(ty) => !infcx.type_moves_by_default(self, ty, span),
                     Err(..) => false,
                 }
             };
@@ -963,8 +964,12 @@ fn is_copy_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 {
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::CopyTraitLangItem);
-    tcx.infer_ctxt(param_env)
-       .enter(|infcx| traits::type_known_to_meet_bound(&infcx, ty, trait_def_id, DUMMY_SP))
+    tcx.infer_ctxt(())
+       .enter(|infcx| traits::type_known_to_meet_bound(&infcx,
+                                                       param_env,
+                                                       ty,
+                                                       trait_def_id,
+                                                       DUMMY_SP))
 }
 
 fn is_sized_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -973,8 +978,12 @@ fn is_sized_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 {
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::SizedTraitLangItem);
-    tcx.infer_ctxt(param_env)
-       .enter(|infcx| traits::type_known_to_meet_bound(&infcx, ty, trait_def_id, DUMMY_SP))
+    tcx.infer_ctxt(())
+       .enter(|infcx| traits::type_known_to_meet_bound(&infcx,
+                                                       param_env,
+                                                       ty,
+                                                       trait_def_id,
+                                                       DUMMY_SP))
 }
 
 fn is_freeze_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -983,8 +992,12 @@ fn is_freeze_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 {
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::FreezeTraitLangItem);
-    tcx.infer_ctxt(param_env)
-       .enter(|infcx| traits::type_known_to_meet_bound(&infcx, ty, trait_def_id, DUMMY_SP))
+    tcx.infer_ctxt(())
+       .enter(|infcx| traits::type_known_to_meet_bound(&infcx,
+                                                       param_env,
+                                                       ty,
+                                                       trait_def_id,
+                                                       DUMMY_SP))
 }
 
 fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs
index d0cbbaf2c10bf..aa2c9802e5473 100644
--- a/src/librustc/ty/wf.rs
+++ b/src/librustc/ty/wf.rs
@@ -26,12 +26,14 @@ use middle::lang_items;
 /// make any progress at all. This is to prevent "livelock" where we
 /// say "$0 is WF if $0 is WF".
 pub fn obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                   param_env: ty::ParamEnv<'tcx>,
                                    body_id: ast::NodeId,
                                    ty: Ty<'tcx>,
                                    span: Span)
                                    -> Option<Vec<traits::PredicateObligation<'tcx>>>
 {
     let mut wf = WfPredicates { infcx: infcx,
+                                param_env: param_env,
                                 body_id: body_id,
                                 span: span,
                                 out: vec![] };
@@ -50,23 +52,25 @@ pub fn obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
 /// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
 /// if `Bar: Eq`.
 pub fn trait_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                         param_env: ty::ParamEnv<'tcx>,
                                          body_id: ast::NodeId,
                                          trait_ref: &ty::TraitRef<'tcx>,
                                          span: Span)
                                          -> Vec<traits::PredicateObligation<'tcx>>
 {
-    let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span, out: vec![] };
+    let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] };
     wf.compute_trait_ref(trait_ref);
     wf.normalize()
 }
 
 pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                             param_env: ty::ParamEnv<'tcx>,
                                              body_id: ast::NodeId,
                                              predicate: &ty::Predicate<'tcx>,
                                              span: Span)
                                              -> Vec<traits::PredicateObligation<'tcx>>
 {
-    let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span, out: vec![] };
+    let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] };
 
     // (*) ok to skip binders, because wf code is prepared for it
     match *predicate {
@@ -126,6 +130,7 @@ pub enum ImpliedBound<'tcx> {
 /// the `ImpliedBound` type for more details.
 pub fn implied_bounds<'a, 'gcx, 'tcx>(
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
     body_id: ast::NodeId,
     ty: Ty<'tcx>,
     span: Span)
@@ -148,7 +153,7 @@ pub fn implied_bounds<'a, 'gcx, 'tcx>(
         // than the ultimate set. (Note: normally there won't be
         // unresolved inference variables here anyway, but there might be
         // during typeck under some circumstances.)
-        let obligations = obligations(infcx, body_id, ty, span).unwrap_or(vec![]);
+        let obligations = obligations(infcx, param_env, body_id, ty, span).unwrap_or(vec![]);
 
         // From the full set of obligations, just filter down to the
         // region relationships.
@@ -231,6 +236,7 @@ fn implied_bounds_from_components<'tcx>(sub_region: ty::Region<'tcx>,
 
 struct WfPredicates<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
     body_id: ast::NodeId,
     span: Span,
     out: Vec<traits::PredicateObligation<'tcx>>,
@@ -244,11 +250,12 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
     fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
         let cause = self.cause(traits::MiscObligation);
         let infcx = &mut self.infcx;
+        let param_env = self.param_env;
         self.out.iter()
                 .inspect(|pred| assert!(!pred.has_escaping_regions()))
                 .flat_map(|pred| {
                     let mut selcx = traits::SelectionContext::new(infcx);
-                    let pred = traits::normalize(&mut selcx, cause.clone(), pred);
+                    let pred = traits::normalize(&mut selcx, param_env, cause.clone(), pred);
                     once(pred.value).chain(pred.obligations)
                 })
                 .collect()
@@ -261,10 +268,12 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
         self.out.extend(obligations);
 
         let cause = self.cause(traits::MiscObligation);
+        let param_env = self.param_env;
         self.out.extend(
             trait_ref.substs.types()
                             .filter(|ty| !ty.has_escaping_regions())
                             .map(|ty| traits::Obligation::new(cause.clone(),
+                                                              param_env,
                                                               ty::Predicate::WellFormed(ty))));
     }
 
@@ -280,7 +289,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
         if !data.has_escaping_regions() {
             let predicate = data.trait_ref.to_predicate();
             let cause = self.cause(traits::ProjectionWf(data));
-            self.out.push(traits::Obligation::new(cause, predicate));
+            self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
         }
     }
 
@@ -291,7 +300,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
                 def_id: self.infcx.tcx.require_lang_item(lang_items::SizedTraitLangItem),
                 substs: self.infcx.tcx.mk_substs_trait(subty, &[]),
             };
-            self.out.push(traits::Obligation::new(cause, trait_ref.to_predicate()));
+            self.out.push(traits::Obligation::new(cause, self.param_env, trait_ref.to_predicate()));
         }
     }
 
@@ -301,6 +310,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
     /// in which case we are not able to simplify at all.
     fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
         let mut subtys = ty0.walk();
+        let param_env = self.param_env;
         while let Some(ty) = subtys.next() {
             match ty.sty {
                 ty::TyBool |
@@ -350,6 +360,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
                         self.out.push(
                             traits::Obligation::new(
                                 cause,
+                                param_env,
                                 ty::Predicate::TypeOutlives(
                                     ty::Binder(
                                         ty::OutlivesPredicate(mt.ty, r)))));
@@ -389,12 +400,12 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
                     // checking those
 
                     let cause = self.cause(traits::MiscObligation);
-
                     let component_traits =
                         data.auto_traits().chain(data.principal().map(|p| p.def_id()));
                     self.out.extend(
                         component_traits.map(|did| traits::Obligation::new(
                             cause.clone(),
+                            param_env,
                             ty::Predicate::ObjectSafe(did)
                         ))
                     );
@@ -422,7 +433,9 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
 
                         let cause = self.cause(traits::MiscObligation);
                         self.out.push( // ...not the type we started from, so we made progress.
-                            traits::Obligation::new(cause, ty::Predicate::WellFormed(ty)));
+                            traits::Obligation::new(cause,
+                                                    self.param_env,
+                                                    ty::Predicate::WellFormed(ty)));
                     } else {
                         // Yes, resolved, proceed with the
                         // result. Should never return false because
@@ -448,7 +461,9 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
         let cause = self.cause(traits::ItemObligation(def_id));
         predicates.predicates
                   .into_iter()
-                  .map(|pred| traits::Obligation::new(cause.clone(), pred))
+                  .map(|pred| traits::Obligation::new(cause.clone(),
+                                                      self.param_env,
+                                                      pred))
                   .filter(|pred| !pred.has_escaping_regions())
                   .collect()
     }
@@ -497,7 +512,9 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
             for implicit_bound in implicit_bounds {
                 let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound));
                 let outlives = ty::Binder(ty::OutlivesPredicate(explicit_bound, implicit_bound));
-                self.out.push(traits::Obligation::new(cause, outlives.to_predicate()));
+                self.out.push(traits::Obligation::new(cause,
+                                                      self.param_env,
+                                                      outlives.to_predicate()));
             }
         }
     }
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index 0fe8865f4a268..122a37ee32aea 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -90,7 +90,7 @@ struct CheckLoanCtxt<'a, 'tcx: 'a> {
     dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
     move_data: &'a move_data::FlowedMoveData<'a, 'tcx>,
     all_loans: &'a [Loan<'tcx>],
-    param_env: &'a ty::ParamEnv<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
@@ -191,15 +191,17 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
                                      body: &hir::Body) {
     debug!("check_loans(body id={})", body.value.id);
 
+    let def_id = bccx.tcx.hir.body_owner_def_id(body.id());
     let infcx = bccx.tcx.borrowck_fake_infer_ctxt(body.id());
+    let param_env = bccx.tcx.param_env(def_id);
     let mut clcx = CheckLoanCtxt {
         bccx: bccx,
         dfcx_loans: dfcx_loans,
         move_data: move_data,
         all_loans: all_loans,
-        param_env: &infcx.param_env
+        param_env,
     };
-    euv::ExprUseVisitor::new(&mut clcx, &bccx.region_maps, &infcx).consume_body(body);
+    euv::ExprUseVisitor::new(&mut clcx, &bccx.region_maps, &infcx, param_env).consume_body(body);
 }
 
 #[derive(PartialEq)]
diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
index 4cfee36359cd7..85a09969ac81c 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
@@ -38,9 +38,10 @@ mod move_error;
 
 pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
                                     body: hir::BodyId)
-                                    -> (Vec<Loan<'tcx>>,
-                                        move_data::MoveData<'tcx>) {
+                                    -> (Vec<Loan<'tcx>>, move_data::MoveData<'tcx>) {
+    let def_id = bccx.tcx.hir.body_owner_def_id(body);
     let infcx = bccx.tcx.borrowck_fake_infer_ctxt(body);
+    let param_env = bccx.tcx.param_env(def_id);
     let mut glcx = GatherLoanCtxt {
         bccx: bccx,
         infcx: &infcx,
@@ -51,7 +52,7 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
     };
 
     let body = glcx.bccx.tcx.hir.body(body);
-    euv::ExprUseVisitor::new(&mut glcx, &bccx.region_maps, &infcx).consume_body(body);
+    euv::ExprUseVisitor::new(&mut glcx, &bccx.region_maps, &infcx, param_env).consume_body(body);
 
     glcx.report_potential_errors();
     let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 2b5bbe0e8a5df..539f1ff3730ee 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -526,7 +526,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                      lp: &LoanPath<'tcx>,
                                      the_move: &move_data::Move,
                                      moved_lp: &LoanPath<'tcx>,
-                                     _param_env: &ty::ParamEnv<'tcx>) {
+                                     _param_env: ty::ParamEnv<'tcx>) {
         let (verb, verb_participle) = match use_kind {
             MovedInUse => ("use", "used"),
             MovedInCapture => ("capture", "captured"),
diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs
index 39db384e2ded2..dd26a3e611f88 100644
--- a/src/librustc_const_eval/check_match.rs
+++ b/src/librustc_const_eval/check_match.rs
@@ -517,11 +517,11 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
 ///
 /// FIXME: this should be done by borrowck.
 fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Expr) {
-    cx.tcx.infer_ctxt((cx.tables, cx.param_env)).enter(|infcx| {
+    cx.tcx.infer_ctxt(cx.tables).enter(|infcx| {
         let mut checker = MutationChecker {
             cx: cx,
         };
-        ExprUseVisitor::new(&mut checker, cx.region_maps, &infcx).walk_expr(guard);
+        ExprUseVisitor::new(&mut checker, cx.region_maps, &infcx, cx.param_env).walk_expr(guard);
     });
 }
 
diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs
index 1364898b549d4..3d07ffc2bc77a 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -483,9 +483,11 @@ fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     debug!("resolve_trait_associated_const: trait_ref={:?}",
            trait_ref);
 
-    tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(()).enter(|infcx| {
+        let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
         let mut selcx = traits::SelectionContext::new(&infcx);
         let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
+                                                 param_env,
                                                  trait_ref.to_poly_trait_predicate());
         let selection = match selcx.select(&obligation) {
             Ok(Some(vtable)) => vtable,
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 619e7ec6a4f60..3a4729e64548a 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -951,12 +951,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
                     let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, callee_substs);
                     let trait_ref = ty::Binder(trait_ref);
                     let span = tcx.hir.span(expr_id);
+                    let param_env = tcx.param_env(method.def_id);
                     let obligation =
                         traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
+                                                param_env,
                                                 trait_ref.to_poly_trait_predicate());
 
-                    let param_env = tcx.param_env(method.def_id);
-                    tcx.infer_ctxt(param_env).enter(|infcx| {
+                    tcx.infer_ctxt(()).enter(|infcx| {
                         let mut selcx = traits::SelectionContext::new(&infcx);
                         match selcx.select(&obligation) {
                             // The method comes from a `T: Trait` bound.
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index e9cf3115ddab1..08a5cb37e5788 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -171,8 +171,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 {
     let span = tcx.hir.span(ctor_id);
     if let hir::VariantData::Tuple(ref fields, ctor_id) = *v {
-        let pe = tcx.param_env(tcx.hir.local_def_id(ctor_id));
-        tcx.infer_ctxt(pe).enter(|infcx| {
+        tcx.infer_ctxt(()).enter(|infcx| {
             let (mut mir, src) =
                 shim::build_adt_ctor(&infcx, ctor_id, fields, span);
 
diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs
index 581a403fb6dee..5b7b52a72b0ab 100644
--- a/src/librustc_mir/hair/cx/mod.rs
+++ b/src/librustc_mir/hair/cx/mod.rs
@@ -35,6 +35,7 @@ use std::rc::Rc;
 pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    pub param_env: ty::ParamEnv<'tcx>,
     pub region_maps: Rc<RegionMaps>,
 
     /// This is `Constness::Const` if we are compiling a `static`,
@@ -64,6 +65,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
         let src_id = src.item_id();
         let src_def_id = tcx.hir.local_def_id(src_id);
 
+        let param_env = tcx.param_env(src_def_id);
         let region_maps = tcx.region_maps(src_def_id);
 
         let attrs = tcx.hir.attrs(src_id);
@@ -80,7 +82,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
         // Constants and const fn's always need overflow checks.
         check_overflow |= constness == hir::Constness::Const;
 
-        Cx { tcx, infcx, region_maps, constness, src, check_overflow }
+        Cx { tcx, infcx, param_env, region_maps, constness, src, check_overflow }
     }
 }
 
@@ -169,12 +171,12 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
     }
 
     pub fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool {
-        let ty = self.tcx.lift_to_global(&ty).unwrap_or_else(|| {
-            bug!("MIR: Cx::needs_drop({}) got \
+        let (ty, param_env) = self.tcx.lift_to_global(&(ty, self.param_env)).unwrap_or_else(|| {
+            bug!("MIR: Cx::needs_drop({:?}, {:?}) got \
                   type with inference types/regions",
-                 ty);
+                 ty, self.param_env);
         });
-        ty.needs_drop(self.tcx.global_tcx(), self.infcx.param_env)
+        ty.needs_drop(self.tcx.global_tcx(), param_env)
     }
 
     pub fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 7c49a11ca1f71..ef88e813a50c6 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -998,10 +998,13 @@ impl MirPass for QualifyAndPromoteConstants {
         // Statics must be Sync.
         if mode == Mode::Static {
             let ty = mir.return_ty;
-            tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
+            tcx.infer_ctxt(()).enter(|infcx| {
+                let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
                 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
                 let mut fulfillment_cx = traits::FulfillmentContext::new();
-                fulfillment_cx.register_bound(&infcx, ty,
+                fulfillment_cx.register_bound(&infcx,
+                                              param_env,
+                                              ty,
                                               tcx.require_lang_item(lang_items::SyncTraitLangItem),
                                               cause);
                 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index f7055f90f0f83..fb162f354e991 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -320,6 +320,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
 
 pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'gcx>,
     fulfillment_cx: traits::FulfillmentContext<'tcx>,
     last_span: Span,
     body_id: ast::NodeId,
@@ -327,12 +328,16 @@ pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 }
 
 impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
-    fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, body_id: ast::NodeId) -> Self {
+    fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+           body_id: ast::NodeId,
+           param_env: ty::ParamEnv<'gcx>)
+           -> Self {
         TypeChecker {
             infcx: infcx,
             fulfillment_cx: traits::FulfillmentContext::new(),
             last_span: DUMMY_SP,
-            body_id: body_id,
+            body_id,
+            param_env,
             reported_errors: FxHashSet(),
         }
     }
@@ -351,14 +356,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     fn sub_types(&mut self, sup: Ty<'tcx>, sub: Ty<'tcx>)
                  -> infer::UnitResult<'tcx>
     {
-        self.infcx.sub_types(false, &self.misc(self.last_span), sup, sub)
+        self.infcx.sub_types(false, &self.misc(self.last_span), self.param_env, sup, sub)
             .map(|ok| self.register_infer_ok_obligations(ok))
     }
 
     fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>)
                 -> infer::UnitResult<'tcx>
     {
-        self.infcx.eq_types(false, &self.misc(span), a, b)
+        self.infcx.eq_types(false, &self.misc(span), self.param_env, a, b)
             .map(|ok| self.register_infer_ok_obligations(ok))
     }
 
@@ -665,7 +670,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
 
         let span = local_decl.source_info.span;
         let ty = local_decl.ty;
-        if !ty.is_sized(self.tcx().global_tcx(), self.infcx.param_env(), span) {
+        if !ty.is_sized(self.tcx().global_tcx(), self.param_env, span) {
             // in current MIR construction, all non-control-flow rvalue
             // expressions evaluate through `as_temp` or `into` a return
             // slot or local, so to find all unsized rvalues it is enough
@@ -706,7 +711,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         let mut selcx = traits::SelectionContext::new(self.infcx);
         let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID);
         let traits::Normalized { value, obligations } =
-            traits::normalize(&mut selcx, cause, value);
+            traits::normalize(&mut selcx, self.param_env, cause, value);
 
         debug!("normalize: value={:?} obligations={:?}",
                value,
@@ -752,8 +757,8 @@ impl MirPass for TypeckMir {
             return;
         }
         let param_env = tcx.param_env(def_id);
-        tcx.infer_ctxt(param_env).enter(|infcx| {
-            let mut checker = TypeChecker::new(&infcx, item_id);
+        tcx.infer_ctxt(()).enter(|infcx| {
+            let mut checker = TypeChecker::new(&infcx, item_id, param_env);
             {
                 let mut verifier = TypeVerifier::new(&mut checker, mir);
                 verifier.visit_mir(mir);
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index 2a4a13932e3fa..65a9334bbae19 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -139,10 +139,10 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
         }
 
         let outer_penv = self.tcx.infer_ctxt(body_id).enter(|infcx| {
-            let param_env = infcx.param_env.clone();
+            let param_env = self.tcx.param_env(item_def_id);
             let outer_penv = mem::replace(&mut self.param_env, param_env);
             let region_maps = &self.tcx.region_maps(item_def_id);
-            euv::ExprUseVisitor::new(self, region_maps, &infcx).consume_body(body);
+            euv::ExprUseVisitor::new(self, region_maps, &infcx, param_env).consume_body(body);
             outer_penv
         });
 
diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs
index 1a13c8e6f3def..151de5ac98df9 100644
--- a/src/librustc_trans/context.rs
+++ b/src/librustc_trans/context.rs
@@ -23,12 +23,12 @@ use monomorphize::Instance;
 use partitioning::CodegenUnit;
 use type_::Type;
 use rustc_data_structures::base_n;
+use rustc::session::config::{self, NoDebugInfo};
+use rustc::session::Session;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::layout::{LayoutCx, LayoutError, LayoutTyper, TyLayout};
-use session::config::{self, NoDebugInfo};
-use session::Session;
-use util::nodemap::{NodeSet, DefIdMap, FxHashMap};
+use rustc::util::nodemap::{NodeSet, DefIdMap, FxHashMap};
 
 use std::ffi::{CStr, CString};
 use std::cell::{Cell, RefCell};
diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs
index 92017465f7d74..6aac9dc42ee02 100644
--- a/src/librustc_typeck/check/autoderef.rs
+++ b/src/librustc_typeck/check/autoderef.rs
@@ -118,13 +118,16 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
         let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
 
         let mut selcx = traits::SelectionContext::new(self.fcx);
-        let obligation = traits::Obligation::new(cause.clone(), trait_ref.to_predicate());
+        let obligation = traits::Obligation::new(cause.clone(),
+                                                 self.fcx.param_env,
+                                                 trait_ref.to_predicate());
         if !selcx.evaluate_obligation(&obligation) {
             debug!("overloaded_deref_ty: cannot match obligation");
             return None;
         }
 
         let normalized = traits::normalize_projection_type(&mut selcx,
+                                                           self.fcx.param_env,
                                                            ty::ProjectionTy::from_ref_and_name(
                                                                tcx,
                                                                trait_ref,
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 72ce7d3b5ed71..91aeade65aa4c 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -555,6 +555,6 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     fn type_is_known_to_be_sized(&self, ty: Ty<'tcx>, span: Span) -> bool {
         let lang_item = self.tcx.require_lang_item(lang_items::SizedTraitLangItem);
-        traits::type_known_to_meet_bound(self, ty, lang_item, span)
+        traits::type_known_to_meet_bound(self, self.param_env, ty, lang_item, span)
     }
 }
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index c2e8269aafef9..f041db43e16d7 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -81,9 +81,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         let fn_sig = self.liberate_late_bound_regions(expr_def_id, &sig);
         let fn_sig = self.inh.normalize_associated_types_in(body.value.span,
-                                                            body.value.id, &fn_sig);
+                                                            body.value.id,
+                                                            self.param_env,
+                                                            &fn_sig);
 
-        check_fn(self, fn_sig, decl, expr.id, body);
+        check_fn(self, self.param_env, fn_sig, decl, expr.id, body);
 
         // Tuple up the arguments and insert the resulting function type into
         // the `closures` table.
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index a962cdb8f728f..b1b4e099626cf 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -137,9 +137,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         self.commit_if_ok(|_| {
             let trace = TypeTrace::types(&self.cause, false, a, b);
             if self.use_lub {
-                self.lub(false, trace, &a, &b)
+                self.lub(false, trace, self.fcx.param_env, &a, &b)
             } else {
-                self.sub(false, trace, &a, &b)
+                self.sub(false, trace, self.fcx.param_env, &a, &b)
             }
         })
     }
@@ -511,9 +511,12 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
 
         // Create an obligation for `Source: CoerceUnsized<Target>`.
         let cause = ObligationCause::misc(self.cause.span, self.body_id);
-        queue.push_back(self.tcx
-            .predicate_for_trait_def(cause, coerce_unsized_did, 0,
-                                     coerce_source, &[coerce_target]));
+        queue.push_back(self.tcx.predicate_for_trait_def(self.fcx.param_env,
+                                                         cause,
+                                                         coerce_unsized_did,
+                                                         0,
+                                                         coerce_source,
+                                                         &[coerce_target]));
 
         // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
         // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
@@ -775,13 +778,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         match (&prev_ty.sty, &new_ty.sty) {
             (&ty::TyFnDef(a_def_id, a_substs, a_fty), &ty::TyFnDef(b_def_id, b_substs, b_fty)) => {
                 // The signature must always match.
-                let fty = self.lub(true, trace.clone(), &a_fty, &b_fty)
+                let fty = self.lub(true, trace.clone(), self.param_env, &a_fty, &b_fty)
                               .map(|ok| self.register_infer_ok_obligations(ok))?;
 
                 if a_def_id == b_def_id {
                     // Same function, maybe the parameters match.
                     let substs = self.commit_if_ok(|_| {
-                        self.lub(true, trace.clone(), &a_substs, &b_substs)
+                        self.lub(true, trace.clone(), self.param_env, &a_substs, &b_substs)
                             .map(|ok| self.register_infer_ok_obligations(ok))
                     });
 
@@ -850,7 +853,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
             if !noop {
                 return self.commit_if_ok(|_| {
-                    self.lub(true, trace.clone(), &prev_ty, &new_ty)
+                    self.lub(true, trace.clone(), self.param_env, &prev_ty, &new_ty)
                         .map(|ok| self.register_infer_ok_obligations(ok))
                 });
             }
@@ -863,7 +866,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     Err(e)
                 } else {
                     self.commit_if_ok(|_| {
-                        self.lub(true, trace, &prev_ty, &new_ty)
+                        self.lub(true, trace, self.param_env, &prev_ty, &new_ty)
                             .map(|ok| self.register_infer_ok_obligations(ok))
                     })
                 }
@@ -1106,7 +1109,7 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
             // Another example is `break` with no argument expression.
             assert!(expression_ty.is_nil());
             assert!(expression_ty.is_nil(), "if let hack without unit type");
-            fcx.eq_types(label_expression_as_expected, cause, expression_ty, self.merged_ty())
+            fcx.eq_types(label_expression_as_expected, cause, fcx.param_env, expression_ty, self.merged_ty())
                .map(|infer_ok| {
                    fcx.register_infer_ok_obligations(infer_ok);
                    expression_ty
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index 1d6d7fa61001a..50e7e89a5f61a 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -219,12 +219,12 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                                          param_env,
                                                          normalize_cause.clone());
 
-    tcx.infer_ctxt(param_env).enter(|infcx| {
+    tcx.infer_ctxt(()).enter(|infcx| {
         let inh = Inherited::new(infcx, impl_m.def_id);
         let infcx = &inh.infcx;
 
         debug!("compare_impl_method: caller_bounds={:?}",
-               infcx.param_env.caller_bounds);
+               param_env.caller_bounds);
 
         let mut selcx = traits::SelectionContext::new(&infcx);
 
@@ -234,10 +234,10 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                                        &ty::Binder(impl_m_own_bounds.predicates));
         for predicate in impl_m_own_bounds {
             let traits::Normalized { value: predicate, obligations } =
-                traits::normalize(&mut selcx, normalize_cause.clone(), &predicate);
+                traits::normalize(&mut selcx, param_env, normalize_cause.clone(), &predicate);
 
             inh.register_predicates(obligations);
-            inh.register_predicate(traits::Obligation::new(cause.clone(), predicate));
+            inh.register_predicate(traits::Obligation::new(cause.clone(), param_env, predicate));
         }
 
         // We now need to check that the signature of the impl method is
@@ -270,6 +270,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         let impl_sig =
             inh.normalize_associated_types_in(impl_m_span,
                                               impl_m_node_id,
+                                              param_env,
                                               &impl_sig);
         let impl_fty = tcx.mk_fn_ptr(ty::Binder(impl_sig));
         debug!("compare_impl_method: impl_fty={:?}", impl_fty);
@@ -282,12 +283,13 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         let trait_sig =
             inh.normalize_associated_types_in(impl_m_span,
                                               impl_m_node_id,
+                                              param_env,
                                               &trait_sig);
         let trait_fty = tcx.mk_fn_ptr(ty::Binder(trait_sig));
 
         debug!("compare_impl_method: trait_fty={:?}", trait_fty);
 
-        let sub_result = infcx.sub_types(false, &cause, impl_fty, trait_fty)
+        let sub_result = infcx.sub_types(false, &cause, param_env, impl_fty, trait_fty)
                               .map(|InferOk { obligations, .. }| {
                                   inh.register_predicates(obligations);
                               });
@@ -298,6 +300,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                    trait_fty);
 
             let (impl_err_span, trait_err_span) = extract_spans_for_error_reporting(&infcx,
+                                                                                    param_env,
                                                                                     &terr,
                                                                                     &cause,
                                                                                     impl_m,
@@ -345,11 +348,10 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             // pass around temporarily.
             let region_maps = RegionMaps::new();
             let mut free_regions = FreeRegionMap::new();
-            free_regions.relate_free_regions_from_predicates(
-                &infcx.param_env.caller_bounds);
+            free_regions.relate_free_regions_from_predicates(&param_env.caller_bounds);
             infcx.resolve_regions_and_report_errors(impl_m.def_id, &region_maps, &free_regions);
         } else {
-            let fcx = FnCtxt::new(&inh, impl_m_node_id);
+            let fcx = FnCtxt::new(&inh, param_env, impl_m_node_id);
             fcx.regionck_item(impl_m_node_id, impl_m_span, &[]);
         }
 
@@ -400,6 +402,7 @@ fn check_region_bounds_on_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 }
 
 fn extract_spans_for_error_reporting<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
+                                                     param_env: ty::ParamEnv<'tcx>,
                                                      terr: &TypeError,
                                                      cause: &ObligationCause<'tcx>,
                                                      impl_m: &ty::AssociatedItem,
@@ -458,14 +461,21 @@ fn extract_spans_for_error_reporting<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a
                          .zip(impl_m_iter)
                          .zip(trait_m_iter)
                          .filter_map(|(((impl_arg_ty, trait_arg_ty), impl_arg), trait_arg)| {
-                             match infcx.sub_types(true, &cause, trait_arg_ty, impl_arg_ty) {
+                             match infcx.sub_types(true,
+                                                   &cause,
+                                                   param_env,
+                                                   trait_arg_ty,
+                                                   impl_arg_ty) {
                                  Ok(_) => None,
                                  Err(_) => Some((impl_arg.span, Some(trait_arg.span))),
                              }
                          })
                          .next()
                          .unwrap_or_else(|| {
-                             if infcx.sub_types(false, &cause, impl_sig.output(),
+                             if infcx.sub_types(false,
+                                                &cause,
+                                                param_env,
+                                                impl_sig.output(),
                                                 trait_sig.output())
                                      .is_err() {
                                          (impl_m_output.span(), Some(trait_m_output.span()))
@@ -714,7 +724,8 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                     impl_trait_ref: ty::TraitRef<'tcx>) {
     debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
 
-    tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(()).enter(|infcx| {
+        let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
         let inh = Inherited::new(infcx, impl_c.def_id);
         let infcx = &inh.infcx;
 
@@ -737,17 +748,19 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         // There is no "body" here, so just pass dummy id.
         let impl_ty = inh.normalize_associated_types_in(impl_c_span,
                                                         impl_c_node_id,
+                                                        param_env,
                                                         &impl_ty);
 
         debug!("compare_const_impl: impl_ty={:?}", impl_ty);
 
         let trait_ty = inh.normalize_associated_types_in(impl_c_span,
                                                          impl_c_node_id,
+                                                         param_env,
                                                          &trait_ty);
 
         debug!("compare_const_impl: trait_ty={:?}", trait_ty);
 
-        let err = infcx.sub_types(false, &cause, impl_ty, trait_ty)
+        let err = infcx.sub_types(false, &cause, param_env, impl_ty, trait_ty)
             .map(|ok| inh.register_infer_ok_obligations(ok));
 
         if let Err(terr) = err {
@@ -795,7 +808,7 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             return;
         }
 
-        let fcx = FnCtxt::new(&inh, impl_c_node_id);
+        let fcx = FnCtxt::new(&inh, param_env, impl_c_node_id);
         fcx.regionck_item(impl_c_node_id, impl_c_span, &[]);
     });
 }
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index d92dafe690459..9c3dcc3f0e0eb 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -27,7 +27,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     // they don't.
     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
         let cause = self.misc(sp);
-        match self.sub_types(false, &cause, actual, expected) {
+        match self.sub_types(false, &cause, self.param_env, actual, expected) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
             },
@@ -54,7 +54,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                      cause: &ObligationCause<'tcx>,
                                      expected: Ty<'tcx>,
                                      actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
-        match self.eq_types(false, cause, actual, expected) {
+        match self.eq_types(false, cause, self.param_env, actual, expected) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
                 None
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 06f405120ae42..024d1f09f1cf7 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -79,8 +79,8 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
 
     // check that the impl type can be made to match the trait type.
 
-    let impl_param_env = tcx.param_env(self_type_did);
-    tcx.infer_ctxt(impl_param_env).enter(|ref infcx| {
+    tcx.infer_ctxt(()).enter(|ref infcx| {
+        let impl_param_env = tcx.param_env(self_type_did);
         let tcx = infcx.tcx;
         let mut fulfillment_cx = traits::FulfillmentContext::new();
 
@@ -92,7 +92,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
         let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs);
 
         let cause = &ObligationCause::misc(drop_impl_span, drop_impl_node_id);
-        match infcx.eq_types(true, cause, named_type, fresh_impl_self_ty) {
+        match infcx.eq_types(true, cause, impl_param_env, named_type, fresh_impl_self_ty) {
             Ok(InferOk { obligations, .. }) => {
                 fulfillment_cx.register_predicate_obligations(infcx, obligations);
             }
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 34e8d6b95a926..a5cf4dd268304 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -340,7 +340,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
     }
 
     fn unify_receivers(&mut self, self_ty: Ty<'tcx>, method_self_ty: Ty<'tcx>) {
-        match self.sub_types(false, &self.misc(self.span), self_ty, method_self_ty) {
+        match self.sub_types(false, &self.misc(self.span), self.param_env, self_ty, method_self_ty) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
             }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index b0ac61d2cc34a..73c1215f275fb 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -205,7 +205,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // Construct an obligation
         let poly_trait_ref = trait_ref.to_poly_trait_ref();
         let obligation =
-            traits::Obligation::misc(span, self.body_id, poly_trait_ref.to_predicate());
+            traits::Obligation::misc(span,
+                                     self.body_id,
+                                     self.param_env,
+                                     poly_trait_ref.to_predicate());
 
         // Now we want to know if this can be matched
         let mut selcx = traits::SelectionContext::new(self);
@@ -262,14 +265,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         assert!(!bounds.has_escaping_regions());
 
         let cause = traits::ObligationCause::misc(span, self.body_id);
-        obligations.extend(traits::predicates_for_generics(cause.clone(), &bounds));
+        obligations.extend(traits::predicates_for_generics(cause.clone(),
+                                                           self.param_env,
+                                                           &bounds));
 
         // Also add an obligation for the method type being well-formed.
         let method_ty = tcx.mk_fn_ptr(ty::Binder(fn_sig));
         debug!("lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
                method_ty,
                obligation);
-        obligations.push(traits::Obligation::new(cause, ty::Predicate::WellFormed(method_ty)));
+        obligations.push(traits::Obligation::new(cause,
+                                                 self.param_env,
+                                                 ty::Predicate::WellFormed(method_ty)));
 
         let callee = MethodCallee {
             def_id: def_id,
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 1a1a9361a89f9..449c0f0281044 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -540,7 +540,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             let cause = traits::ObligationCause::misc(self.span, self.body_id);
             let mut selcx = &mut traits::SelectionContext::new(self.fcx);
             let traits::Normalized { value: xform_self_ty, obligations } =
-                traits::normalize(selcx, cause, &xform_self_ty);
+                traits::normalize(selcx, self.param_env, cause, &xform_self_ty);
             debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}",
                    xform_self_ty);
 
@@ -679,7 +679,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                     let output = fty.output().subst(self.tcx, substs);
                     let (output, _) = self.replace_late_bound_regions_with_fresh_var(
                         self.span, infer::FnCall, &output);
-                    self.can_sub_types(output, expected).is_ok()
+                    self.can_sub_types(self.param_env, output, expected).is_ok()
                 })
             }
             _ => false,
@@ -751,7 +751,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             let cause = traits::ObligationCause::misc(self.span, self.body_id);
             let mut selcx = &mut traits::SelectionContext::new(self.fcx);
             let traits::Normalized { value: xform_self_ty, obligations } =
-                traits::normalize(selcx, cause, &xform_self_ty);
+                traits::normalize(selcx, self.param_env, cause, &xform_self_ty);
 
             debug!("xform_self_ty={:?}", xform_self_ty);
 
@@ -885,7 +885,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                        substs,
                        bound);
 
-                if self.can_equate(&step.self_ty, &bound.self_ty()).is_ok() {
+                if self.can_equate(self.param_env, &step.self_ty, &bound.self_ty()).is_ok() {
                     let xform_self_ty = self.xform_self_ty(&item, bound.self_ty(), bound.substs);
 
                     debug!("assemble_projection_candidates: bound={:?} xform_self_ty={:?}",
@@ -1145,6 +1145,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             // First check that the self type can be related.
             let sub_obligations = match self.sub_types(false,
                                                        &ObligationCause::dummy(),
+                                                       self.param_env,
                                                        self_ty,
                                                        probe.xform_self_ty) {
                 Ok(InferOk { obligations, value: () }) => obligations,
@@ -1182,10 +1183,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             let impl_bounds = self.tcx.predicates_of(impl_def_id);
             let impl_bounds = impl_bounds.instantiate(self.tcx, substs);
             let traits::Normalized { value: impl_bounds, obligations: norm_obligations } =
-                traits::normalize(selcx, cause.clone(), &impl_bounds);
+                traits::normalize(selcx, self.param_env, cause.clone(), &impl_bounds);
 
             // Convert the bounds into obligations.
-            let obligations = traits::predicates_for_generics(cause.clone(), &impl_bounds);
+            let obligations = traits::predicates_for_generics(cause.clone(), self.param_env, &impl_bounds);
             debug!("impl_obligations={:?}", obligations);
 
             // Evaluate those obligations to see if they might possibly hold.
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 594f1813a5ab4..c496053a1543b 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -57,7 +57,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs);
                         let poly_trait_ref = trait_ref.to_poly_trait_ref();
                         let obligation =
-                            Obligation::misc(span, self.body_id, poly_trait_ref.to_predicate());
+                            Obligation::misc(span,
+                                             self.body_id,
+                                             self.param_env,
+                                             poly_trait_ref.to_predicate());
                         SelectionContext::new(self).evaluate_obligation(&obligation)
                     })
                 })
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 8074c0630e395..055ce94d20105 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -450,6 +450,14 @@ impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> {
 pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     body_id: ast::NodeId,
 
+    /// The parameter environment used for proving trait obligations
+    /// in this function. This can change when we descend into
+    /// closures (as they bring new things into scope), hence it is
+    /// not part of `Inherited` (as of the time of this writing,
+    /// closures do not yet change the environment, but they will
+    /// eventually).
+    param_env: ty::ParamEnv<'tcx>,
+
     // Number of errors that had been reported when we started
     // checking this function. On exit, if we find that *more* errors
     // have been reported, we will skip regionck and other work that
@@ -528,9 +536,8 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
     pub fn build(tcx: TyCtxt<'a, 'gcx, 'gcx>, def_id: DefId)
                  -> InheritedBuilder<'a, 'gcx, 'tcx> {
         let tables = ty::TypeckTables::empty();
-        let param_env = tcx.param_env(def_id);
         InheritedBuilder {
-            infcx: tcx.infer_ctxt((tables, param_env)),
+            infcx: tcx.infer_ctxt(tables),
             def_id,
         }
     }
@@ -590,16 +597,18 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
     fn normalize_associated_types_in<T>(&self,
                                         span: Span,
                                         body_id: ast::NodeId,
+                                        param_env: ty::ParamEnv<'tcx>,
                                         value: &T) -> T
         where T : TypeFoldable<'tcx>
     {
-        let ok = self.normalize_associated_types_in_as_infer_ok(span, body_id, value);
+        let ok = self.normalize_associated_types_in_as_infer_ok(span, body_id, param_env, value);
         self.register_infer_ok_obligations(ok)
     }
 
     fn normalize_associated_types_in_as_infer_ok<T>(&self,
                                                     span: Span,
                                                     body_id: ast::NodeId,
+                                                    param_env: ty::ParamEnv<'tcx>,
                                                     value: &T)
                                                     -> InferOk<'tcx, T>
         where T : TypeFoldable<'tcx>
@@ -608,7 +617,7 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
         let mut selcx = traits::SelectionContext::new(self);
         let cause = ObligationCause::misc(span, body_id);
         let traits::Normalized { value, obligations } =
-            traits::normalize(&mut selcx, cause, value);
+            traits::normalize(&mut selcx, param_env, cause, value);
         debug!("normalize_associated_types_in: result={:?} predicates={:?}",
             value,
             obligations);
@@ -797,6 +806,7 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let body = tcx.hir.body(body_id);
 
     Inherited::build(tcx, def_id).enter(|inh| {
+        let param_env = tcx.param_env(def_id);
         let fcx = if let Some(decl) = fn_decl {
             let fn_sig = tcx.type_of(def_id).fn_sig();
 
@@ -806,11 +816,14 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             let fn_sig =
                 inh.liberate_late_bound_regions(def_id, &fn_sig);
             let fn_sig =
-                inh.normalize_associated_types_in(body.value.span, body_id.node_id, &fn_sig);
+                inh.normalize_associated_types_in(body.value.span,
+                                                  body_id.node_id,
+                                                  param_env,
+                                                  &fn_sig);
 
-            check_fn(&inh, fn_sig, decl, id, body)
+            check_fn(&inh, param_env, fn_sig, decl, id, body)
         } else {
-            let fcx = FnCtxt::new(&inh, body.value.id);
+            let fcx = FnCtxt::new(&inh, param_env, body.value.id);
             let expected_type = tcx.type_of(def_id);
             let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type);
             fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
@@ -919,6 +932,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> {
 /// * ...
 /// * inherited: other fields inherited from the enclosing fn (if any)
 fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
+                            param_env: ty::ParamEnv<'tcx>,
                             fn_sig: ty::FnSig<'tcx>,
                             decl: &'gcx hir::FnDecl,
                             fn_id: ast::NodeId,
@@ -927,11 +941,11 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
 {
     let mut fn_sig = fn_sig.clone();
 
-    debug!("check_fn(sig={:?}, fn_id={})", fn_sig, fn_id);
+    debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env);
 
     // Create the function context.  This is either derived from scratch or,
     // in the case of function expressions, based on the outer context.
-    let mut fcx = FnCtxt::new(inherited, body.value.id);
+    let mut fcx = FnCtxt::new(inherited, param_env, body.value.id);
     *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
 
     let ret_ty = fn_sig.output();
@@ -1633,10 +1647,12 @@ enum TupleArgumentsFlag {
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>,
+               param_env: ty::ParamEnv<'tcx>,
                body_id: ast::NodeId)
                -> FnCtxt<'a, 'gcx, 'tcx> {
         FnCtxt {
             body_id: body_id,
+            param_env,
             err_count_on_creation: inh.tcx.sess.err_count(),
             ret_coercion: None,
             ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal,
@@ -1870,7 +1886,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     // Require that the predicate holds for the concrete type.
                     let cause = traits::ObligationCause::new(span, self.body_id,
                                                              traits::ReturnType);
-                    self.register_predicate(traits::Obligation::new(cause, predicate));
+                    self.register_predicate(traits::Obligation::new(cause,
+                                                                    self.param_env,
+                                                                    predicate));
                 }
 
                 ty_var
@@ -1883,15 +1901,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
         where T : TypeFoldable<'tcx>
     {
-        let ok = self.normalize_associated_types_in_as_infer_ok(span, value);
-        self.register_infer_ok_obligations(ok)
+        self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
     }
 
     fn normalize_associated_types_in_as_infer_ok<T>(&self, span: Span, value: &T)
                                                     -> InferOk<'tcx, T>
         where T : TypeFoldable<'tcx>
     {
-        self.inh.normalize_associated_types_in_as_infer_ok(span, self.body_id, value)
+        self.inh.normalize_associated_types_in_as_infer_ok(span, self.body_id, self.param_env, value)
     }
 
     pub fn write_nil(&self, node_id: ast::NodeId) {
@@ -1929,7 +1946,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                           cause: traits::ObligationCause<'tcx>)
     {
         self.fulfillment_cx.borrow_mut()
-            .register_bound(self, ty, def_id, cause);
+                           .register_bound(self, self.param_env, ty, def_id, cause);
     }
 
     pub fn to_ty(&self, ast_t: &hir::Ty) -> Ty<'tcx> {
@@ -1970,7 +1987,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     {
         // WF obligations never themselves fail, so no real need to give a detailed cause:
         let cause = traits::ObligationCause::new(span, self.body_id, code);
-        self.register_predicate(traits::Obligation::new(cause, ty::Predicate::WellFormed(ty)));
+        self.register_predicate(traits::Obligation::new(cause,
+                                                        self.param_env,
+                                                        ty::Predicate::WellFormed(ty)));
     }
 
     pub fn register_old_wf_obligation(&self,
@@ -2023,7 +2042,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         debug!("add_obligations_for_parameters(predicates={:?})",
                predicates);
 
-        for obligation in traits::predicates_for_generics(cause, predicates) {
+        for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
             self.register_predicate(obligation);
         }
     }
@@ -2704,7 +2723,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // is polymorphic) and the expected return type.
                 // No argument expectations are produced if unification fails.
                 let origin = self.misc(call_span);
-                let ures = self.sub_types(false, &origin, formal_ret, ret_ty);
+                let ures = self.sub_types(false, &origin, self.param_env, formal_ret, ret_ty);
 
                 // FIXME(#15760) can't use try! here, FromError doesn't default
                 // to identity so the resulting type is not constrained.
@@ -4199,7 +4218,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             _ => return,
         };
         let last_expr_ty = self.expr_ty(last_expr);
-        if self.can_sub_types(last_expr_ty, expected_ty).is_err() {
+        if self.can_sub_types(self.param_env, last_expr_ty, expected_ty).is_err() {
             return;
         }
         let original_span = original_sp(last_stmt.span, blk.span);
@@ -4459,7 +4478,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             let ty = self.tcx.type_of(impl_def_id);
 
             let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
-            match self.sub_types(false, &self.misc(span), self_ty, impl_ty) {
+            match self.sub_types(false, &self.misc(span), self.param_env, self_ty, impl_ty) {
                 Ok(ok) => self.register_infer_ok_obligations(ok),
                 Err(_) => {
                     span_bug!(span,
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 8e5b7a6546973..4d69b37b113cf 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -238,9 +238,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             lhs_ty);
 
                         if let TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty {
-                            if !self.infcx.type_moves_by_default(ty_mut.ty, lhs_expr.span) &&
-                                self.lookup_op_method(ty_mut.ty, &[rhs_ty],
-                                                      Op::Binary(op, is_assign)).is_ok() {
+                            if {
+                                !self.infcx.type_moves_by_default(self.param_env,
+                                                                  ty_mut.ty,
+                                                                  lhs_expr.span) &&
+                                    self.lookup_op_method(ty_mut.ty,
+                                                          &[rhs_ty],
+                                                          Op::Binary(op, is_assign))
+                                        .is_ok()
+                            } {
                                 err.note(
                                     &format!(
                                         "this is a reference to a type that `{}` can be applied \
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index afc003b986d41..db817efe93d4a 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -386,7 +386,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         for &ty in fn_sig_tys {
             let ty = self.resolve_type(ty);
             debug!("relate_free_regions(t={:?})", ty);
-            let implied_bounds = ty::wf::implied_bounds(self, body_id, ty, span);
+            let implied_bounds = ty::wf::implied_bounds(self, self.fcx.param_env, body_id, ty, span);
 
             // Record any relations between free regions that we observe into the free-region-map.
             self.free_region_map.relate_free_regions_from_implied_bounds(&implied_bounds);
@@ -1660,7 +1660,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
 
                     // check whether this predicate applies to our current projection
                     let cause = self.fcx.misc(span);
-                    match self.eq_types(false, &cause, ty, outlives.0) {
+                    match self.eq_types(false, &cause, self.fcx.param_env, ty, outlives.0) {
                         Ok(ok) => {
                             self.register_infer_ok_obligations(ok);
                             Ok(outlives.1)
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 286d0ad1b35a3..25f5418bea9c5 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -166,9 +166,11 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
         {
             let body_owner_def_id = self.fcx.tcx.hir.body_owner_def_id(body.id());
             let region_maps = &self.fcx.tcx.region_maps(body_owner_def_id);
+            let param_env = self.fcx.param_env;
             let mut euv =
                 euv::ExprUseVisitor::with_options(self,
                                                   self.fcx,
+                                                  param_env,
                                                   region_maps,
                                                   mc::MemCategorizationOptions {
                                                       during_closure_kind_inference: true
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 6895d73862562..26f708e934562 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -37,7 +37,8 @@ struct CheckWfFcxBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     inherited: super::InheritedBuilder<'a, 'gcx, 'tcx>,
     code: ObligationCauseCode<'gcx>,
     id: ast::NodeId,
-    span: Span
+    span: Span,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 impl<'a, 'gcx, 'tcx> CheckWfFcxBuilder<'a, 'gcx, 'tcx> {
@@ -48,8 +49,9 @@ impl<'a, 'gcx, 'tcx> CheckWfFcxBuilder<'a, 'gcx, 'tcx> {
         let code = self.code.clone();
         let id = self.id;
         let span = self.span;
+        let param_env = self.param_env;
         self.inherited.enter(|inh| {
-            let fcx = FnCtxt::new(&inh, id);
+            let fcx = FnCtxt::new(&inh, param_env, id);
             let wf_tys = f(&fcx, &mut CheckTypeWellFormedVisitor {
                 tcx: fcx.tcx.global_tcx(),
                 code: code
@@ -206,11 +208,13 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
 
     fn for_id<'tcx>(&self, id: ast::NodeId, span: Span)
                     -> CheckWfFcxBuilder<'a, 'gcx, 'tcx> {
+        let def_id = self.tcx.hir.local_def_id(id);
         CheckWfFcxBuilder {
-            inherited: Inherited::build(self.tcx, self.tcx.hir.local_def_id(id)),
+            inherited: Inherited::build(self.tcx, def_id),
             code: self.code.clone(),
             id: id,
-            span: span
+            span: span,
+            param_env: self.tcx.param_env(def_id),
         }
     }
 
@@ -374,6 +378,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
                             ast_trait_ref.path.span, &trait_ref);
                     let obligations =
                         ty::wf::trait_obligations(fcx,
+                                                  fcx.param_env,
                                                   fcx.body_id,
                                                   &trait_ref,
                                                   ast_trait_ref.path.span);
@@ -405,6 +410,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
             predicates.predicates
                       .iter()
                       .flat_map(|p| ty::wf::predicate_obligations(fcx,
+                                                                  fcx.param_env,
                                                                   fcx.body_id,
                                                                   p,
                                                                   span));
diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs
index 89f2595d1a8fb..8c2679f1bbf53 100644
--- a/src/librustc_typeck/coherence/builtin.rs
+++ b/src/librustc_typeck/coherence/builtin.rs
@@ -208,7 +208,7 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
            source,
            target);
 
-    tcx.infer_ctxt(param_env).enter(|infcx| {
+    tcx.infer_ctxt(()).enter(|infcx| {
         let cause = ObligationCause::misc(span, impl_node_id);
         let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
                            mt_b: ty::TypeAndMut<'tcx>,
@@ -308,7 +308,7 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         // we may have to evaluate constraint
                         // expressions in the course of execution.)
                         // See e.g. #41936.
-                        if let Ok(ok) = infcx.eq_types(false, &cause, b, a) {
+                        if let Ok(ok) = infcx.eq_types(false, &cause, param_env, b, a) {
                             if ok.obligations.is_empty() {
                                 return None;
                             }
@@ -376,7 +376,12 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         // Register an obligation for `A: Trait<B>`.
         let cause = traits::ObligationCause::misc(span, impl_node_id);
-        let predicate = tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
+        let predicate = tcx.predicate_for_trait_def(param_env,
+                                                    cause,
+                                                    trait_def_id,
+                                                    0,
+                                                    source,
+                                                    &[target]);
         fulfill_cx.register_predicate_obligation(&infcx, predicate);
 
         // Check that all transitive obligations are satisfied.
@@ -387,8 +392,7 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         // Finally, resolve all regions.
         let region_maps = RegionMaps::new();
         let mut free_regions = FreeRegionMap::new();
-        free_regions.relate_free_regions_from_predicates(&infcx.param_env
-            .caller_bounds);
+        free_regions.relate_free_regions_from_predicates(&param_env.caller_bounds);
         infcx.resolve_regions_and_report_errors(impl_did, &region_maps, &free_regions);
 
         CoerceUnsizedInfo {
diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
index 4aa12d08f61c5..afeb85a7a0656 100644
--- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs
+++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
@@ -11,7 +11,7 @@
 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc::hir;
 use rustc::hir::itemlikevisit::ItemLikeVisitor;
-use rustc::traits::{self, Reveal};
+use rustc::traits;
 use rustc::ty::{self, TyCtxt};
 
 pub fn crate_inherent_impls_overlap_check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -70,7 +70,7 @@ impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
 
         for (i, &impl1_def_id) in impls.iter().enumerate() {
             for &impl2_def_id in &impls[(i + 1)..] {
-                self.tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
+                self.tcx.infer_ctxt(()).enter(|infcx| {
                     if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() {
                         self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id)
                     }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 91dec958a161c..ac0e360adb08a 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -155,9 +155,10 @@ fn require_same_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                 expected: Ty<'tcx>,
                                 actual: Ty<'tcx>)
                                 -> bool {
-    tcx.infer_ctxt(Reveal::UserFacing).enter(|ref infcx| {
+    tcx.infer_ctxt(()).enter(|ref infcx| {
+        let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
         let mut fulfill_cx = FulfillmentContext::new();
-        match infcx.eq_types(false, &cause, expected, actual) {
+        match infcx.eq_types(false, &cause, param_env, expected, actual) {
             Ok(InferOk { obligations, .. }) => {
                 fulfill_cx.register_predicate_obligations(infcx, obligations);
             }

From c1e895d92c83bd626dece3410b015253e55fdb74 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 22 May 2017 14:20:12 -0400
Subject: [PATCH 4/8] remove `layout_cache` and fix `-Z print-type-sizes`

now we grow the type-sizes info during execution, rather than walking
the cache after the fact
---
 src/librustc/ty/context.rs                   |   4 -
 src/librustc/ty/layout.rs                    | 217 ++++++++++++++++++-
 src/librustc/ty/util.rs                      |  16 +-
 src/librustc_trans/base.rs                   | 194 +----------------
 src/test/{compile-fail => ui}/issue-26548.rs |   0
 src/test/ui/issue-26548.stderr               |   9 +
 6 files changed, 240 insertions(+), 200 deletions(-)
 rename src/test/{compile-fail => ui}/issue-26548.rs (100%)
 create mode 100644 src/test/ui/issue-26548.stderr

diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 87cee2ddc03f8..c83f73bff6949 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -519,9 +519,6 @@ pub struct GlobalCtxt<'tcx> {
     /// Data layout specification for the current target.
     pub data_layout: TargetDataLayout,
 
-    /// Cache for layouts computed from types.
-    pub layout_cache: RefCell<FxHashMap<Ty<'tcx>, &'tcx Layout>>,
-
     /// Used to prevent layout from recursing too deeply.
     pub layout_depth: Cell<usize>,
 
@@ -718,7 +715,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             rvalue_promotable_to_static: RefCell::new(NodeMap()),
             crate_name: Symbol::intern(crate_name),
             data_layout: data_layout,
-            layout_cache: RefCell::new(FxHashMap()),
             layout_interner: RefCell::new(FxHashSet()),
             layout_depth: Cell::new(0),
             derive_macros: RefCell::new(NodeMap()),
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 4dc764f6d30c2..c37fb0f5a778d 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -12,10 +12,10 @@ pub use self::Integer::*;
 pub use self::Layout::*;
 pub use self::Primitive::*;
 
-use session::Session;
+use session::{self, DataTypeKind, Session};
 use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
 
-use syntax::ast::{FloatTy, IntTy, UintTy};
+use syntax::ast::{self, FloatTy, IntTy, UintTy};
 use syntax::attr;
 use syntax_pos::DUMMY_SP;
 
@@ -1690,6 +1690,219 @@ impl<'a, 'tcx> Layout {
             }
         }
     }
+
+    /// This is invoked by the `layout_raw` query to record the final
+    /// layout of each type.
+    #[inline]
+    pub fn record_layout_for_printing(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                      ty: Ty<'tcx>,
+                                      param_env: ty::ParamEnv<'tcx>,
+                                      layout: &Layout) {
+        // If we are running with `-Zprint-type-sizes`, record layouts for
+        // dumping later. Ignore layouts that are done with non-empty
+        // environments or non-monomorphic layouts, as the user only wants
+        // to see the stuff resulting from the final trans session.
+        if
+            !tcx.sess.opts.debugging_opts.print_type_sizes ||
+            ty.has_param_types() ||
+            ty.has_self_ty() ||
+            !param_env.caller_bounds.is_empty()
+        {
+            return;
+        }
+
+        Self::record_layout_for_printing_outlined(tcx, ty, param_env, layout)
+    }
+
+    fn record_layout_for_printing_outlined(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                           ty: Ty<'tcx>,
+                                           param_env: ty::ParamEnv<'tcx>,
+                                           layout: &Layout) {
+        // (delay format until we actually need it)
+        let record = |kind, opt_discr_size, variants| {
+            let type_desc = format!("{:?}", ty);
+            let overall_size = layout.size(tcx);
+            let align = layout.align(tcx);
+            tcx.sess.code_stats.borrow_mut().record_type_size(kind,
+                                                              type_desc,
+                                                              align,
+                                                              overall_size,
+                                                              opt_discr_size,
+                                                              variants);
+        };
+
+        let (adt_def, substs) = match ty.sty {
+            ty::TyAdt(ref adt_def, substs) => {
+                debug!("print-type-size t: `{:?}` process adt", ty);
+                (adt_def, substs)
+            }
+
+            ty::TyClosure(..) => {
+                debug!("print-type-size t: `{:?}` record closure", ty);
+                record(DataTypeKind::Closure, None, vec![]);
+                return;
+            }
+
+            _ => {
+                debug!("print-type-size t: `{:?}` skip non-nominal", ty);
+                return;
+            }
+        };
+
+        let adt_kind = adt_def.adt_kind();
+
+        let build_field_info = |(field_name, field_ty): (ast::Name, Ty<'tcx>), offset: &Size| {
+            let layout = field_ty.layout(tcx, param_env);
+            match layout {
+                Err(_) => bug!("no layout found for field {} type: `{:?}`", field_name, field_ty),
+                Ok(field_layout) => {
+                    session::FieldInfo {
+                        name: field_name.to_string(),
+                        offset: offset.bytes(),
+                        size: field_layout.size(tcx).bytes(),
+                        align: field_layout.align(tcx).abi(),
+                    }
+                }
+            }
+        };
+
+        let build_primitive_info = |name: ast::Name, value: &Primitive| {
+            session::VariantInfo {
+                name: Some(name.to_string()),
+                kind: session::SizeKind::Exact,
+                align: value.align(tcx).abi(),
+                size: value.size(tcx).bytes(),
+                fields: vec![],
+            }
+        };
+
+        enum Fields<'a> {
+            WithDiscrim(&'a Struct),
+            NoDiscrim(&'a Struct),
+        }
+
+        let build_variant_info = |n: Option<ast::Name>,
+                                  flds: &[(ast::Name, Ty<'tcx>)],
+                                  layout: Fields| {
+            let (s, field_offsets) = match layout {
+                Fields::WithDiscrim(s) => (s, &s.offsets[1..]),
+                Fields::NoDiscrim(s) => (s, &s.offsets[0..]),
+            };
+            let field_info: Vec<_> = flds.iter()
+                                         .zip(field_offsets.iter())
+                                         .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset))
+                                         .collect();
+
+            session::VariantInfo {
+                name: n.map(|n|n.to_string()),
+                kind: if s.sized {
+                    session::SizeKind::Exact
+                } else {
+                    session::SizeKind::Min
+                },
+                align: s.align.abi(),
+                size: s.min_size.bytes(),
+                fields: field_info,
+            }
+        };
+
+        match *layout {
+            Layout::StructWrappedNullablePointer { nonnull: ref variant_layout,
+                                                   nndiscr,
+                                                   discrfield: _,
+                                                   discrfield_source: _ } => {
+                debug!("print-type-size t: `{:?}` adt struct-wrapped nullable nndiscr {} is {:?}",
+                       ty, nndiscr, variant_layout);
+                let variant_def = &adt_def.variants[nndiscr as usize];
+                let fields: Vec<_> = variant_def.fields.iter()
+                                                       .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
+                                                       .collect();
+                record(adt_kind.into(),
+                       None,
+                       vec![build_variant_info(Some(variant_def.name),
+                                               &fields,
+                                               Fields::NoDiscrim(variant_layout))]);
+            }
+            Layout::RawNullablePointer { nndiscr, value } => {
+                debug!("print-type-size t: `{:?}` adt raw nullable nndiscr {} is {:?}",
+                       ty, nndiscr, value);
+                let variant_def = &adt_def.variants[nndiscr as usize];
+                record(adt_kind.into(), None,
+                       vec![build_primitive_info(variant_def.name, &value)]);
+            }
+            Layout::Univariant { variant: ref variant_layout, non_zero: _ } => {
+                let variant_names = || {
+                    adt_def.variants.iter().map(|v|format!("{}", v.name)).collect::<Vec<_>>()
+                };
+                debug!("print-type-size t: `{:?}` adt univariant {:?} variants: {:?}",
+                       ty, variant_layout, variant_names());
+                assert!(adt_def.variants.len() <= 1,
+                        "univariant with variants {:?}", variant_names());
+                if adt_def.variants.len() == 1 {
+                    let variant_def = &adt_def.variants[0];
+                    let fields: Vec<_> = variant_def.fields.iter()
+                                                           .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
+                                                           .collect();
+                    record(adt_kind.into(),
+                           None,
+                           vec![build_variant_info(Some(variant_def.name),
+                                                   &fields,
+                                                   Fields::NoDiscrim(variant_layout))]);
+                } else {
+                    // (This case arises for *empty* enums; so give it
+                    // zero variants.)
+                    record(adt_kind.into(), None, vec![]);
+                }
+            }
+
+            Layout::General { ref variants, discr, .. } => {
+                debug!("print-type-size t: `{:?}` adt general variants def {} layouts {} {:?}",
+                       ty, adt_def.variants.len(), variants.len(), variants);
+                let variant_infos: Vec<_> = adt_def.variants.iter()
+                                                            .zip(variants.iter())
+                                                            .map(|(variant_def, variant_layout)| {
+                                                                let fields: Vec<_> = variant_def.fields.iter()
+                                                                                                       .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
+                                                                                                       .collect();
+                                                                build_variant_info(Some(variant_def.name),
+                                                                                   &fields,
+                                                                                   Fields::WithDiscrim(variant_layout))
+                                                            })
+                                                            .collect();
+                record(adt_kind.into(), Some(discr.size()), variant_infos);
+            }
+
+            Layout::UntaggedUnion { ref variants } => {
+                debug!("print-type-size t: `{:?}` adt union variants {:?}",
+                       ty, variants);
+                // layout does not currently store info about each
+                // variant...
+                record(adt_kind.into(), None, Vec::new());
+            }
+
+            Layout::CEnum { discr, .. } => {
+                debug!("print-type-size t: `{:?}` adt c-like enum", ty);
+                let variant_infos: Vec<_> =
+                    adt_def.variants.iter()
+                                    .map(|variant_def| {
+                                        build_primitive_info(variant_def.name,
+                                                             &Primitive::Int(discr))
+                                    })
+                                    .collect();
+                record(adt_kind.into(), Some(discr.size()), variant_infos);
+            }
+
+            // other cases provide little interesting (i.e. adjustable
+            // via representation tweaks) size info beyond total size.
+            Layout::Scalar { .. } |
+            Layout::Vector { .. } |
+            Layout::Array { .. } |
+            Layout::FatPointer { .. } => {
+                debug!("print-type-size t: `{:?}` adt other", ty);
+                record(adt_kind.into(), None, Vec::new())
+            }
+        }
+    }
 }
 
 /// Type size "skeleton", i.e. the only information determining a type's size.
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 8c2577a382431..ec4ca54d6f5ed 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -799,9 +799,22 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
                         param_env: ty::ParamEnv<'tcx>)
                         -> Result<&'tcx Layout, LayoutError<'tcx>> {
         let ty = tcx.erase_regions(&self);
-        tcx.layout_raw(param_env.reveal_all().and(ty))
+        let layout = tcx.layout_raw(param_env.reveal_all().and(ty));
+
+        // NB: This recording is normally disabled; when enabled, it
+        // can however trigger recursive invocations of `layout()`.
+        // Therefore, we execute it *after* the main query has
+        // completed, to avoid problems around recursive structures
+        // and the like. (Admitedly, I wasn't able to reproduce a problem
+        // here, but it seems like the right thing to do. -nmatsakis)
+        if let Ok(l) = layout {
+            Layout::record_layout_for_printing(tcx, ty, param_env, l);
+        }
+
+        layout
     }
 
+
     /// Check whether a type is representable. This means it cannot contain unboxed
     /// structural recursion. This check is needed for structs and enums.
     pub fn is_representable(&'tcx self,
@@ -1084,6 +1097,7 @@ fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     tcx.layout_depth.set(depth+1);
     let layout = Layout::compute_uncached(tcx, param_env, ty);
     tcx.layout_depth.set(depth);
+
     layout
 }
 
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 1b3f0ba7ce5b6..88c30cd866599 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -44,7 +44,7 @@ use rustc::middle::cstore::LinkMeta;
 use rustc::hir::map as hir_map;
 use rustc::util::common::time;
 use rustc::session::config::{self, NoDebugInfo};
-use rustc::session::{self, DataTypeKind, Session};
+use rustc::session::Session;
 use rustc_incremental::IncrementalHashesMap;
 use abi;
 use mir::lvalue::LvalueRef;
@@ -80,7 +80,6 @@ use std::i32;
 use syntax_pos::Span;
 use syntax::attr;
 use rustc::hir;
-use rustc::ty::layout::{self, Layout};
 use syntax::ast;
 
 use mir::lvalue::Alignment;
@@ -1287,10 +1286,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                             &exported_symbols);
     });
 
-    if tcx.sess.opts.debugging_opts.print_type_sizes {
-        gather_type_sizes(tcx);
-    }
-
     if sess.target.target.options.is_like_msvc &&
        sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) {
         create_imps(sess, &llvm_modules);
@@ -1322,193 +1317,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
 }
 
-fn gather_type_sizes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
-    let layout_cache = tcx.layout_cache.borrow();
-    for (ty, layout) in layout_cache.iter() {
-
-        // (delay format until we actually need it)
-        let record = |kind, opt_discr_size, variants| {
-            let type_desc = format!("{:?}", ty);
-            let overall_size = layout.size(tcx);
-            let align = layout.align(tcx);
-            tcx.sess.code_stats.borrow_mut().record_type_size(kind,
-                                                              type_desc,
-                                                              align,
-                                                              overall_size,
-                                                              opt_discr_size,
-                                                              variants);
-        };
-
-        let (adt_def, substs) = match ty.sty {
-            ty::TyAdt(ref adt_def, substs) => {
-                debug!("print-type-size t: `{:?}` process adt", ty);
-                (adt_def, substs)
-            }
-
-            ty::TyClosure(..) => {
-                debug!("print-type-size t: `{:?}` record closure", ty);
-                record(DataTypeKind::Closure, None, vec![]);
-                continue;
-            }
-
-            _ => {
-                debug!("print-type-size t: `{:?}` skip non-nominal", ty);
-                continue;
-            }
-        };
-
-        let adt_kind = adt_def.adt_kind();
-
-        let build_field_info = |(field_name, field_ty): (ast::Name, Ty), offset: &layout::Size| {
-            match layout_cache.get(&field_ty) {
-                None => bug!("no layout found for field {} type: `{:?}`", field_name, field_ty),
-                Some(field_layout) => {
-                    session::FieldInfo {
-                        name: field_name.to_string(),
-                        offset: offset.bytes(),
-                        size: field_layout.size(tcx).bytes(),
-                        align: field_layout.align(tcx).abi(),
-                    }
-                }
-            }
-        };
-
-        let build_primitive_info = |name: ast::Name, value: &layout::Primitive| {
-            session::VariantInfo {
-                name: Some(name.to_string()),
-                kind: session::SizeKind::Exact,
-                align: value.align(tcx).abi(),
-                size: value.size(tcx).bytes(),
-                fields: vec![],
-            }
-        };
-
-        enum Fields<'a> {
-            WithDiscrim(&'a layout::Struct),
-            NoDiscrim(&'a layout::Struct),
-        }
-
-        let build_variant_info = |n: Option<ast::Name>, flds: &[(ast::Name, Ty)], layout: Fields| {
-            let (s, field_offsets) = match layout {
-                Fields::WithDiscrim(s) => (s, &s.offsets[1..]),
-                Fields::NoDiscrim(s) => (s, &s.offsets[0..]),
-            };
-            let field_info: Vec<_> = flds.iter()
-                .zip(field_offsets.iter())
-                .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset))
-                .collect();
-
-            session::VariantInfo {
-                name: n.map(|n|n.to_string()),
-                kind: if s.sized {
-                    session::SizeKind::Exact
-                } else {
-                    session::SizeKind::Min
-                },
-                align: s.align.abi(),
-                size: s.min_size.bytes(),
-                fields: field_info,
-            }
-        };
-
-        match **layout {
-            Layout::StructWrappedNullablePointer { nonnull: ref variant_layout,
-                                                   nndiscr,
-                                                   discrfield: _,
-                                                   discrfield_source: _ } => {
-                debug!("print-type-size t: `{:?}` adt struct-wrapped nullable nndiscr {} is {:?}",
-                       ty, nndiscr, variant_layout);
-                let variant_def = &adt_def.variants[nndiscr as usize];
-                let fields: Vec<_> = variant_def.fields.iter()
-                    .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
-                    .collect();
-                record(adt_kind.into(),
-                       None,
-                       vec![build_variant_info(Some(variant_def.name),
-                                               &fields,
-                                               Fields::NoDiscrim(variant_layout))]);
-            }
-            Layout::RawNullablePointer { nndiscr, value } => {
-                debug!("print-type-size t: `{:?}` adt raw nullable nndiscr {} is {:?}",
-                       ty, nndiscr, value);
-                let variant_def = &adt_def.variants[nndiscr as usize];
-                record(adt_kind.into(), None,
-                       vec![build_primitive_info(variant_def.name, &value)]);
-            }
-            Layout::Univariant { variant: ref variant_layout, non_zero: _ } => {
-                let variant_names = || {
-                    adt_def.variants.iter().map(|v|format!("{}", v.name)).collect::<Vec<_>>()
-                };
-                debug!("print-type-size t: `{:?}` adt univariant {:?} variants: {:?}",
-                       ty, variant_layout, variant_names());
-                assert!(adt_def.variants.len() <= 1,
-                        "univariant with variants {:?}", variant_names());
-                if adt_def.variants.len() == 1 {
-                    let variant_def = &adt_def.variants[0];
-                    let fields: Vec<_> = variant_def.fields.iter()
-                        .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
-                        .collect();
-                    record(adt_kind.into(),
-                           None,
-                           vec![build_variant_info(Some(variant_def.name),
-                                                   &fields,
-                                                   Fields::NoDiscrim(variant_layout))]);
-                } else {
-                    // (This case arises for *empty* enums; so give it
-                    // zero variants.)
-                    record(adt_kind.into(), None, vec![]);
-                }
-            }
-
-            Layout::General { ref variants, discr, .. } => {
-                debug!("print-type-size t: `{:?}` adt general variants def {} layouts {} {:?}",
-                       ty, adt_def.variants.len(), variants.len(), variants);
-                let variant_infos: Vec<_> = adt_def.variants.iter()
-                    .zip(variants.iter())
-                    .map(|(variant_def, variant_layout)| {
-                        let fields: Vec<_> = variant_def.fields.iter()
-                            .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
-                            .collect();
-                        build_variant_info(Some(variant_def.name),
-                                           &fields,
-                                           Fields::WithDiscrim(variant_layout))
-                    })
-                    .collect();
-                record(adt_kind.into(), Some(discr.size()), variant_infos);
-            }
-
-            Layout::UntaggedUnion { ref variants } => {
-                debug!("print-type-size t: `{:?}` adt union variants {:?}",
-                       ty, variants);
-                // layout does not currently store info about each
-                // variant...
-                record(adt_kind.into(), None, Vec::new());
-            }
-
-            Layout::CEnum { discr, .. } => {
-                debug!("print-type-size t: `{:?}` adt c-like enum", ty);
-                let variant_infos: Vec<_> = adt_def.variants.iter()
-                    .map(|variant_def| {
-                        build_primitive_info(variant_def.name,
-                                             &layout::Primitive::Int(discr))
-                    })
-                    .collect();
-                record(adt_kind.into(), Some(discr.size()), variant_infos);
-            }
-
-            // other cases provide little interesting (i.e. adjustable
-            // via representation tweaks) size info beyond total size.
-            Layout::Scalar { .. } |
-            Layout::Vector { .. } |
-            Layout::Array { .. } |
-            Layout::FatPointer { .. } => {
-                debug!("print-type-size t: `{:?}` adt other", ty);
-                record(adt_kind.into(), None, Vec::new())
-            }
-        }
-    }
-}
-
 #[inline(never)] // give this a place in the profiler
 fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trans_items: I)
     where I: Iterator<Item=&'a TransItem<'tcx>>
diff --git a/src/test/compile-fail/issue-26548.rs b/src/test/ui/issue-26548.rs
similarity index 100%
rename from src/test/compile-fail/issue-26548.rs
rename to src/test/ui/issue-26548.rs
diff --git a/src/test/ui/issue-26548.stderr b/src/test/ui/issue-26548.stderr
new file mode 100644
index 0000000000000..8bfe4ac733b6d
--- /dev/null
+++ b/src/test/ui/issue-26548.stderr
@@ -0,0 +1,9 @@
+error[E0391]: unsupported cyclic reference between types/traits detected
+  |
+note: the cycle begins when computing layout of `S`...
+note: ...which then requires computing layout of `std::option::Option<<S as Mirror>::It>`...
+note: ...which then requires computing layout of `<S as Mirror>::It`...
+  = note: ...which then again requires computing layout of `S`, completing the cycle.
+
+error: aborting due to previous error
+

From c7a2e32c102057242950697da6b83b2190b00dfb Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 24 May 2017 09:43:20 -0400
Subject: [PATCH 5/8] ergonomic improvements to the methods in infcx

---
 src/librustc/infer/at.rs                    | 310 ++++++++++++++++++++
 src/librustc/infer/mod.rs                   | 191 ++----------
 src/librustc/traits/coherence.rs            |   9 +-
 src/librustc/traits/error_reporting.rs      |   8 +-
 src/librustc/traits/project.rs              |  32 +-
 src/librustc/traits/select.rs               |  65 ++--
 src/librustc/traits/specialize/mod.rs       |   7 +-
 src/librustc_mir/transform/type_check.rs    |  12 +-
 src/librustc_typeck/check/coercion.rs       |  29 +-
 src/librustc_typeck/check/compare_method.rs |  35 +--
 src/librustc_typeck/check/demand.rs         |   6 +-
 src/librustc_typeck/check/dropck.rs         |   2 +-
 src/librustc_typeck/check/method/confirm.rs |   2 +-
 src/librustc_typeck/check/method/probe.rs   |  11 +-
 src/librustc_typeck/check/mod.rs            |   6 +-
 src/librustc_typeck/check/regionck.rs       |   2 +-
 src/librustc_typeck/coherence/builtin.rs    |   2 +-
 src/librustc_typeck/lib.rs                  |   2 +-
 18 files changed, 434 insertions(+), 297 deletions(-)
 create mode 100644 src/librustc/infer/at.rs

diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs
new file mode 100644
index 0000000000000..756e0b5f9fb6a
--- /dev/null
+++ b/src/librustc/infer/at.rs
@@ -0,0 +1,310 @@
+// 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.
+
+//! A nice interface for working with the infcx.  The basic idea is to
+//! do `infcx.at(cause, param_env)`, which sets the "cause" of the
+//! operation as well as the surrounding parameter environment.  Then
+//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a
+//! subtype or equality relationship respectively. The first argument
+//! is always the "expected" output from the POV of diagnostics.
+//!
+//! Examples:
+//!
+//!     infcx.at(cause, param_env).sub(a, b)
+//!     // requires that `a <: b`, with `a` considered the "expected" type
+//!
+//!     infcx.at(cause, param_env).sup(a, b)
+//!     // requires that `b <: a`, with `a` considered the "expected" type
+//!
+//!     infcx.at(cause, param_env).eq(a, b)
+//!     // requires that `a == b`, with `a` considered the "expected" type
+//!
+//! For finer-grained control, you can also do use `trace`:
+//!
+//!     infcx.at(...).trace(a, b).sub(&c, &d)
+//!
+//! This will set `a` and `b` as the "root" values for
+//! error-reporting, but actually operate on `c` and `d`. This is
+//! sometimes useful when the types of `c` and `d` are not traceable
+//! things. (That system should probably be refactored.)
+
+use super::*;
+
+use ty::relate::{Relate, TypeRelation};
+
+pub struct At<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    cause: &'a ObligationCause<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+pub struct Trace<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    at: At<'a, 'gcx, 'tcx>,
+    a_is_expected: bool,
+    trace: TypeTrace<'tcx>,
+}
+
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    pub fn at(&'a self,
+              cause: &'a ObligationCause<'tcx>,
+              param_env: ty::ParamEnv<'tcx>)
+              -> At<'a, 'gcx, 'tcx>
+    {
+        At { infcx: self, cause, param_env }
+    }
+}
+
+pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
+    fn to_trace(cause: &ObligationCause<'tcx>,
+                a_is_expected: bool,
+                a: Self,
+                b: Self)
+                -> TypeTrace<'tcx>;
+}
+
+impl<'a, 'gcx, 'tcx> At<'a, 'gcx, 'tcx> {
+    /// Hacky routine for equating two impl headers in coherence.
+    pub fn eq_impl_headers(self,
+                           expected: &ty::ImplHeader<'tcx>,
+                           actual: &ty::ImplHeader<'tcx>)
+                           -> InferResult<'tcx, ()>
+    {
+        debug!("eq_impl_header({:?} = {:?})", expected, actual);
+        match (expected.trait_ref, actual.trait_ref) {
+            (Some(a_ref), Some(b_ref)) =>
+                self.eq(a_ref, b_ref),
+            (None, None) =>
+                self.eq(expected.self_ty, actual.self_ty),
+            _ =>
+                bug!("mk_eq_impl_headers given mismatched impl kinds"),
+        }
+    }
+
+    /// Make `a <: b` where `a` may or may not be expected
+    pub fn sub_exp<T>(self,
+                      a_is_expected: bool,
+                      a: T,
+                      b: T)
+                      -> InferResult<'tcx, ()>
+        where T: ToTrace<'tcx>
+    {
+        self.trace_exp(a_is_expected, a, b).sub(&a, &b)
+    }
+
+    /// Make `actual <: expected`. For example, if type-checking a
+    /// call like `foo(x)`, where `foo: fn(i32)`, you might have
+    /// `sup(i32, x)`, since the "expected" type is the type that
+    /// appears in the signature.
+    pub fn sup<T>(self,
+                  expected: T,
+                  actual: T)
+                  -> InferResult<'tcx, ()>
+        where T: ToTrace<'tcx>
+    {
+        self.sub_exp(false, actual, expected)
+    }
+
+    /// Make `expected <: actual`
+    pub fn sub<T>(self,
+                  expected: T,
+                  actual: T)
+                  -> InferResult<'tcx, ()>
+        where T: ToTrace<'tcx>
+    {
+        self.sub_exp(true, expected, actual)
+    }
+
+    /// Make `expected <: actual`
+    pub fn eq_exp<T>(self,
+                     a_is_expected: bool,
+                     a: T,
+                     b: T)
+                     -> InferResult<'tcx, ()>
+        where T: ToTrace<'tcx>
+    {
+        self.trace_exp(a_is_expected, a, b).eq(&a, &b)
+    }
+
+    /// Make `expected <: actual`
+    pub fn eq<T>(self,
+                 expected: T,
+                 actual: T)
+                 -> InferResult<'tcx, ()>
+        where T: ToTrace<'tcx>
+    {
+        self.trace(expected, actual).eq(&expected, &actual)
+    }
+
+    /// Compute the least-upper-bound, or mutual supertype, of two
+    /// values. The order of the arguments doesn't matter, but since
+    /// this can result in an error (e.g., if asked to compute LUB of
+    /// u32 and i32), it is meaningful to call one of them the
+    /// "expected type".
+    pub fn lub<T>(self,
+                  expected: T,
+                  actual: T)
+                  -> InferResult<'tcx, T>
+        where T: ToTrace<'tcx>
+    {
+        self.trace(expected, actual).lub(&expected, &actual)
+    }
+
+    /// Compute the greatest-lower-bound, or mutual subtype, of two
+    /// values. As with `lub` order doesn't matter, except for error
+    /// cases.
+    pub fn glb<T>(self,
+                  expected: T,
+                  actual: T)
+                  -> InferResult<'tcx, T>
+        where T: ToTrace<'tcx>
+    {
+        self.trace(expected, actual).glb(&expected, &actual)
+    }
+
+    /// Sets the "trace" values that will be used for
+    /// error-repporting, but doesn't actually perform any operation
+    /// yet (this is useful when you want to set the trace using
+    /// distinct values from those you wish to operate upon).
+    pub fn trace<T>(self,
+                    expected: T,
+                    actual: T)
+                    -> Trace<'a, 'gcx, 'tcx>
+        where T: ToTrace<'tcx>
+    {
+        self.trace_exp(true, expected, actual)
+    }
+
+    /// Like `trace`, but the expected value is determined by the
+    /// boolean argument (if true, then the first argument `a` is the
+    /// "expected" value).
+    pub fn trace_exp<T>(self,
+                        a_is_expected: bool,
+                        a: T,
+                        b: T)
+                        -> Trace<'a, 'gcx, 'tcx>
+        where T: ToTrace<'tcx>
+    {
+        let trace = ToTrace::to_trace(self.cause, a_is_expected, a, b);
+        Trace { at: self, trace: trace, a_is_expected }
+    }
+}
+
+impl<'a, 'gcx, 'tcx> Trace<'a, 'gcx, 'tcx> {
+    /// Make `a <: b` where `a` may or may not be expected (if
+    /// `a_is_expected` is true, then `a` is expected).
+    /// Make `expected <: actual`
+    pub fn sub<T>(self,
+                  a: &T,
+                  b: &T)
+                  -> InferResult<'tcx, ()>
+        where T: Relate<'tcx>
+    {
+        debug!("sub({:?} <: {:?})", a, b);
+        let Trace { at, trace, a_is_expected } = self;
+        at.infcx.commit_if_ok(|_| {
+            let mut fields = at.infcx.combine_fields(trace, at.param_env);
+            fields.sub(a_is_expected)
+                  .relate(a, b)
+                  .map(move |_| InferOk { value: (), obligations: fields.obligations })
+        })
+    }
+
+    /// Make `a == b`; the expectation is set by the call to
+    /// `trace()`.
+    pub fn eq<T>(self,
+                 a: &T,
+                 b: &T)
+                 -> InferResult<'tcx, ()>
+        where T: Relate<'tcx>
+    {
+        debug!("eq({:?} == {:?})", a, b);
+        let Trace { at, trace, a_is_expected } = self;
+        at.infcx.commit_if_ok(|_| {
+            let mut fields = at.infcx.combine_fields(trace, at.param_env);
+            fields.equate(a_is_expected)
+                  .relate(a, b)
+                  .map(move |_| InferOk { value: (), obligations: fields.obligations })
+        })
+    }
+
+    pub fn lub<T>(self,
+                  a: &T,
+                  b: &T)
+                  -> InferResult<'tcx, T>
+        where T: Relate<'tcx>
+    {
+        debug!("lub({:?} \\/ {:?})", a, b);
+        let Trace { at, trace, a_is_expected } = self;
+        at.infcx.commit_if_ok(|_| {
+            let mut fields = at.infcx.combine_fields(trace, at.param_env);
+            fields.lub(a_is_expected)
+                  .relate(a, b)
+                  .map(move |t| InferOk { value: t, obligations: fields.obligations })
+        })
+    }
+
+    pub fn glb<T>(self,
+                  a: &T,
+                  b: &T)
+                  -> InferResult<'tcx, T>
+        where T: Relate<'tcx>
+    {
+        debug!("glb({:?} /\\ {:?})", a, b);
+        let Trace { at, trace, a_is_expected } = self;
+        at.infcx.commit_if_ok(|_| {
+            let mut fields = at.infcx.combine_fields(trace, at.param_env);
+            fields.glb(a_is_expected)
+                  .relate(a, b)
+                  .map(move |t| InferOk { value: t, obligations: fields.obligations })
+        })
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
+    fn to_trace(cause: &ObligationCause<'tcx>,
+                a_is_expected: bool,
+                a: Self,
+                b: Self)
+                -> TypeTrace<'tcx>
+    {
+        TypeTrace {
+            cause: cause.clone(),
+            values: Types(ExpectedFound::new(a_is_expected, a, b))
+        }
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {
+    fn to_trace(cause: &ObligationCause<'tcx>,
+                a_is_expected: bool,
+                a: Self,
+                b: Self)
+                -> TypeTrace<'tcx>
+    {
+        TypeTrace {
+            cause: cause.clone(),
+            values: TraitRefs(ExpectedFound::new(a_is_expected, a, b))
+        }
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for ty::PolyTraitRef<'tcx> {
+    fn to_trace(cause: &ObligationCause<'tcx>,
+                a_is_expected: bool,
+                a: Self,
+                b: Self)
+                -> TypeTrace<'tcx>
+    {
+        TypeTrace {
+            cause: cause.clone(),
+            values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b))
+        }
+    }
+}
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index a16f7fb208175..f5869b8a20fab 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -31,7 +31,7 @@ use ty::{TyVid, IntVid, FloatVid};
 use ty::{self, Ty, TyCtxt};
 use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
-use ty::relate::{Relate, RelateResult, TypeRelation};
+use ty::relate::RelateResult;
 use traits::{self, ObligationCause, PredicateObligations, Reveal};
 use rustc_data_structures::unify::{self, UnificationTable};
 use std::cell::{Cell, RefCell, Ref, RefMut};
@@ -49,6 +49,7 @@ use self::region_inference::{RegionVarBindings, RegionSnapshot};
 use self::type_variable::TypeVariableOrigin;
 use self::unify_key::ToType;
 
+pub mod at;
 mod combine;
 mod equate;
 pub mod error_reporting;
@@ -802,62 +803,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    pub fn equate<T>(&'a self,
-                     a_is_expected: bool,
-                     trace: TypeTrace<'tcx>,
-                     param_env: ty::ParamEnv<'tcx>,
-                     a: &T,
-                     b: &T)
-        -> InferResult<'tcx, T>
-        where T: Relate<'tcx>
-    {
-        let mut fields = self.combine_fields(trace, param_env);
-        let result = fields.equate(a_is_expected).relate(a, b);
-        result.map(move |t| InferOk { value: t, obligations: fields.obligations })
-    }
-
-    pub fn sub<T>(&'a self,
-                  a_is_expected: bool,
-                  trace: TypeTrace<'tcx>,
-                  param_env: ty::ParamEnv<'tcx>,
-                  a: &T,
-                  b: &T)
-        -> InferResult<'tcx, T>
-        where T: Relate<'tcx>
-    {
-        let mut fields = self.combine_fields(trace, param_env);
-        let result = fields.sub(a_is_expected).relate(a, b);
-        result.map(move |t| InferOk { value: t, obligations: fields.obligations })
-    }
-
-    pub fn lub<T>(&'a self,
-                  a_is_expected: bool,
-                  trace: TypeTrace<'tcx>,
-                  param_env: ty::ParamEnv<'tcx>,
-                  a: &T,
-                  b: &T)
-        -> InferResult<'tcx, T>
-        where T: Relate<'tcx>
-    {
-        let mut fields = self.combine_fields(trace, param_env);
-        let result = fields.lub(a_is_expected).relate(a, b);
-        result.map(move |t| InferOk { value: t, obligations: fields.obligations })
-    }
-
-    pub fn glb<T>(&'a self,
-                  a_is_expected: bool,
-                  trace: TypeTrace<'tcx>,
-                  param_env: ty::ParamEnv<'tcx>,
-                  a: &T,
-                  b: &T)
-        -> InferResult<'tcx, T>
-        where T: Relate<'tcx>
-    {
-        let mut fields = self.combine_fields(trace, param_env);
-        let result = fields.glb(a_is_expected).relate(a, b);
-        result.map(move |t| InferOk { value: t, obligations: fields.obligations })
-    }
-
     // Clear the "currently in a snapshot" flag, invoke the closure,
     // then restore the flag to its original value. This flag is a
     // debugging measure designed to detect cases where we start a
@@ -1017,102 +962,35 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.region_vars.add_given(sub, sup);
     }
 
-    pub fn sub_types(&self,
-                     a_is_expected: bool,
-                     cause: &ObligationCause<'tcx>,
-                     param_env: ty::ParamEnv<'tcx>,
-                     a: Ty<'tcx>,
-                     b: Ty<'tcx>)
-                     -> InferResult<'tcx, ()>
-    {
-        debug!("sub_types({:?} <: {:?})", a, b);
-        self.commit_if_ok(|_| {
-            let trace = TypeTrace::types(cause, a_is_expected, a, b);
-            self.sub(a_is_expected, trace, param_env, &a, &b).map(|ok| ok.unit())
-        })
-    }
-
-    pub fn can_sub_types(&self,
-                         param_env: ty::ParamEnv<'tcx>,
-                         a: Ty<'tcx>,
-                         b: Ty<'tcx>)
-                         -> UnitResult<'tcx>
+    pub fn can_sub<T>(&self,
+                      param_env: ty::ParamEnv<'tcx>,
+                      a: T,
+                      b: T)
+                      -> UnitResult<'tcx>
+        where T: at::ToTrace<'tcx>
     {
+        let origin = &ObligationCause::dummy();
         self.probe(|_| {
-            let origin = &ObligationCause::dummy();
-            let trace = TypeTrace::types(origin, true, a, b);
-            self.sub(true, trace, param_env, &a, &b).map(|InferOk { obligations: _, .. }| {
+            self.at(origin, param_env).sub(a, b).map(|InferOk { obligations: _, .. }| {
                 // Ignore obligations, since we are unrolling
                 // everything anyway.
             })
         })
     }
 
-    pub fn eq_types(&self,
-                    a_is_expected: bool,
-                    cause: &ObligationCause<'tcx>,
-                    param_env: ty::ParamEnv<'tcx>,
-                    a: Ty<'tcx>,
-                    b: Ty<'tcx>)
-        -> InferResult<'tcx, ()>
+    pub fn can_eq<T>(&self,
+                      param_env: ty::ParamEnv<'tcx>,
+                      a: T,
+                      b: T)
+                      -> UnitResult<'tcx>
+        where T: at::ToTrace<'tcx>
     {
-        self.commit_if_ok(|_| {
-            let trace = TypeTrace::types(cause, a_is_expected, a, b);
-            self.equate(a_is_expected, trace, param_env, &a, &b).map(|ok| ok.unit())
-        })
-    }
-
-    pub fn eq_trait_refs(&self,
-                         a_is_expected: bool,
-                         cause: &ObligationCause<'tcx>,
-                         param_env: ty::ParamEnv<'tcx>,
-                         a: ty::TraitRef<'tcx>,
-                         b: ty::TraitRef<'tcx>)
-        -> InferResult<'tcx, ()>
-    {
-        debug!("eq_trait_refs({:?} = {:?})", a, b);
-        self.commit_if_ok(|_| {
-            let trace = TypeTrace {
-                cause: cause.clone(),
-                values: TraitRefs(ExpectedFound::new(a_is_expected, a, b))
-            };
-            self.equate(a_is_expected, trace, param_env, &a, &b).map(|ok| ok.unit())
-        })
-    }
-
-    pub fn eq_impl_headers(&self,
-                           a_is_expected: bool,
-                           cause: &ObligationCause<'tcx>,
-                           param_env: ty::ParamEnv<'tcx>,
-                           a: &ty::ImplHeader<'tcx>,
-                           b: &ty::ImplHeader<'tcx>)
-                           -> InferResult<'tcx, ()>
-    {
-        debug!("eq_impl_header({:?} = {:?})", a, b);
-        match (a.trait_ref, b.trait_ref) {
-            (Some(a_ref), Some(b_ref)) =>
-                self.eq_trait_refs(a_is_expected, cause, param_env, a_ref, b_ref),
-            (None, None) =>
-                self.eq_types(a_is_expected, cause, param_env, a.self_ty, b.self_ty),
-            _ => bug!("mk_eq_impl_headers given mismatched impl kinds"),
-        }
-    }
-
-    pub fn sub_poly_trait_refs(&self,
-                               a_is_expected: bool,
-                               cause: ObligationCause<'tcx>,
-                               param_env: ty::ParamEnv<'tcx>,
-                               a: ty::PolyTraitRef<'tcx>,
-                               b: ty::PolyTraitRef<'tcx>)
-        -> InferResult<'tcx, ()>
-    {
-        debug!("sub_poly_trait_refs({:?} <: {:?})", a, b);
-        self.commit_if_ok(|_| {
-            let trace = TypeTrace {
-                cause: cause,
-                values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b))
-            };
-            self.sub(a_is_expected, trace, param_env, &a, &b).map(|ok| ok.unit())
+        let origin = &ObligationCause::dummy();
+        self.probe(|_| {
+            self.at(origin, param_env).eq(a, b).map(|InferOk { obligations: _, .. }| {
+                // Ignore obligations, since we are unrolling
+                // everything anyway.
+            })
         })
     }
 
@@ -1134,7 +1012,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             let (ty::EquatePredicate(a, b), skol_map) =
                 self.skolemize_late_bound_regions(predicate, snapshot);
             let cause_span = cause.span;
-            let eqty_ok = self.eq_types(false, cause, param_env, a, b)?;
+            let eqty_ok = self.at(cause, param_env).eq(b, a)?;
             self.leak_check(false, cause_span, &skol_map, snapshot)?;
             self.pop_skolemized(skol_map, snapshot);
             Ok(eqty_ok.unit())
@@ -1172,7 +1050,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 self.skolemize_late_bound_regions(predicate, snapshot);
 
             let cause_span = cause.span;
-            let ok = self.sub_types(a_is_expected, cause, param_env, a, b)?;
+            let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?;
             self.leak_check(false, cause_span, &skol_map, snapshot)?;
             self.pop_skolemized(skol_map, snapshot);
             Ok(ok.unit())
@@ -1606,27 +1484,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.region_vars.verify_generic_bound(origin, kind, a, bound);
     }
 
-    pub fn can_equate<T>(&self, param_env: ty::ParamEnv<'tcx>, a: &T, b: &T) -> UnitResult<'tcx>
-        where T: Relate<'tcx> + fmt::Debug
-    {
-        debug!("can_equate({:?}, {:?})", a, b);
-        self.probe(|_| {
-            // Gin up a dummy trace, since this won't be committed
-            // anyhow. We should make this typetrace stuff more
-            // generic so we don't have to do anything quite this
-            // terrible.
-            let trace = TypeTrace::dummy(self.tcx);
-            self.equate(true, trace, param_env, a, b).map(|InferOk { obligations: _, .. }| {
-                // We can intentionally ignore obligations here, since
-                // this is part of a simple test for general
-                // "equatability". However, it's not entirely clear
-                // that we *ought* to be, perhaps a better thing would
-                // be to use a mini-fulfillment context or something
-                // like that.
-            })
-        })
-    }
-
     pub fn node_ty(&self, id: ast::NodeId) -> McResult<Ty<'tcx>> {
         let ty = self.node_type(id);
         self.resolve_type_vars_or_error(&ty)
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs
index c7d069053e9d0..54fe3a42b6179 100644
--- a/src/librustc/traits/coherence.rs
+++ b/src/librustc/traits/coherence.rs
@@ -84,12 +84,9 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     debug!("overlap: b_impl_header={:?}", b_impl_header);
 
     // Do `a` and `b` unify? If not, no overlap.
-    let obligations = match selcx.infcx().eq_impl_headers(true,
-                                                          &ObligationCause::dummy(),
-                                                          param_env,
-                                                          &a_impl_header,
-                                                          &b_impl_header) {
-        Ok(InferOk { obligations, .. }) => {
+    let obligations = match selcx.infcx().at(&ObligationCause::dummy(), param_env)
+                                         .eq_impl_headers(&a_impl_header, &b_impl_header) {
+        Ok(InferOk { obligations, value: () }) => {
             obligations
         }
         Err(_) => return None
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 6635a1f1f271e..049d5e488c946 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -184,10 +184,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     obligation.cause.clone(),
                     0
                 );
-                if let Err(error) = self.eq_types(
-                    false, &obligation.cause, obligation.param_env,
-                    data.ty, normalized.value
-                ) {
+                if let Err(error) = self.at(&obligation.cause, obligation.param_env)
+                                        .eq(normalized.value, data.ty) {
                     values = Some(infer::ValuePairs::Types(ExpectedFound {
                         expected: normalized.value,
                         found: data.ty,
@@ -269,7 +267,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
                 let impl_self_ty = impl_trait_ref.self_ty();
 
-                if let Ok(..) = self.can_equate(param_env, &trait_self_ty, &impl_self_ty) {
+                if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) {
                     self_match_impls.push(def_id);
 
                     if trait_ref.substs.types().skip(1)
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index c155d1bf241b5..dbeaf46e8be0d 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -181,11 +181,8 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
            obligations);
 
     let infcx = selcx.infcx();
-    match infcx.eq_types(true,
-                         &obligation.cause,
-                         obligation.param_env,
-                         normalized_ty,
-                         obligation.predicate.ty) {
+    match infcx.at(&obligation.cause, obligation.param_env)
+               .eq(normalized_ty, obligation.predicate.ty) {
         Ok(InferOk { obligations: inferred_obligations, value: () }) => {
             obligations.extend(inferred_obligations);
             Ok(Some(obligations))
@@ -840,16 +837,13 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
                         data.to_poly_trait_ref();
                     let obligation_poly_trait_ref =
                         obligation_trait_ref.to_poly_trait_ref();
-                    infcx.sub_poly_trait_refs(false,
-                                              obligation.cause.clone(),
-                                              obligation.param_env,
-                                              data_poly_trait_ref,
-                                              obligation_poly_trait_ref)
-                        .map(|InferOk { obligations: _, value: () }| {
-                            // FIXME(#32730) -- do we need to take obligations
-                            // into account in any way? At the moment, no.
-                        })
-                        .is_ok()
+                    infcx.at(&obligation.cause, obligation.param_env)
+                         .sup(obligation_poly_trait_ref, data_poly_trait_ref)
+                         .map(|InferOk { obligations: _, value: () }| {
+                             // FIXME(#32730) -- do we need to take obligations
+                             // into account in any way? At the moment, no.
+                         })
+                         .is_ok()
                 });
 
                 debug!("assemble_candidates_from_predicates: candidate={:?} \
@@ -1110,11 +1104,9 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
             let data_poly_trait_ref = data.to_poly_trait_ref();
             let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
             selcx.infcx().probe(|_| {
-                selcx.infcx().sub_poly_trait_refs(false,
-                                                  obligation.cause.clone(),
-                                                  obligation.param_env,
-                                                  data_poly_trait_ref,
-                                                  obligation_poly_trait_ref).is_ok()
+                selcx.infcx().at(&obligation.cause, obligation.param_env)
+                             .sup(obligation_poly_trait_ref, data_poly_trait_ref)
+                             .is_ok()
             })
         });
 
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 21d4df3d0990d..6ac26e413ce4a 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -1269,12 +1269,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         -> bool
     {
         assert!(!skol_trait_ref.has_escaping_regions());
-        let cause = obligation.cause.clone();
-        match self.infcx.sub_poly_trait_refs(false,
-                                             cause,
-                                             obligation.param_env,
-                                             trait_bound.clone(),
-                                             ty::Binder(skol_trait_ref.clone())) {
+        match self.infcx.at(&obligation.cause, obligation.param_env)
+                        .sup(ty::Binder(skol_trait_ref), trait_bound) {
             Ok(InferOk { obligations, .. }) => {
                 self.inferred_obligations.extend(obligations);
             }
@@ -2436,11 +2432,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                -> Result<(), SelectionError<'tcx>>
     {
         let obligation_trait_ref = obligation_trait_ref.clone();
-        self.infcx.sub_poly_trait_refs(false,
-                                       obligation_cause.clone(),
-                                       obligation_param_env,
-                                       expected_trait_ref.clone(),
-                                       obligation_trait_ref.clone())
+        self.infcx
+            .at(&obligation_cause, obligation_param_env)
+            .sup(obligation_trait_ref, expected_trait_ref)
             .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
             .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e))
     }
@@ -2475,12 +2469,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 let new_trait = tcx.mk_dynamic(
                     ty::Binder(tcx.mk_existential_predicates(iter)), r_b);
                 let InferOk { obligations, .. } =
-                    self.infcx.eq_types(false,
-                                        &obligation.cause,
-                                        obligation.param_env,
-                                        new_trait,
-                                        target)
-                    .map_err(|_| Unimplemented)?;
+                    self.infcx.at(&obligation.cause, obligation.param_env)
+                              .eq(target, new_trait)
+                              .map_err(|_| Unimplemented)?;
                 self.inferred_obligations.extend(obligations);
 
                 // Register one obligation for 'a: 'b.
@@ -2540,8 +2531,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // [T; n] -> [T].
             (&ty::TyArray(a, _), &ty::TySlice(b)) => {
                 let InferOk { obligations, .. } =
-                    self.infcx.eq_types(false, &obligation.cause, obligation.param_env, a, b)
-                    .map_err(|_| Unimplemented)?;
+                    self.infcx.at(&obligation.cause, obligation.param_env)
+                              .eq(b, a)
+                              .map_err(|_| Unimplemented)?;
                 self.inferred_obligations.extend(obligations);
             }
 
@@ -2603,12 +2595,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 });
                 let new_struct = tcx.mk_adt(def, tcx.mk_substs(params));
                 let InferOk { obligations, .. } =
-                    self.infcx.eq_types(false,
-                                        &obligation.cause,
-                                        obligation.param_env,
-                                        new_struct,
-                                        target)
-                    .map_err(|_| Unimplemented)?;
+                    self.infcx.at(&obligation.cause, obligation.param_env)
+                              .eq(target, new_struct)
+                              .map_err(|_| Unimplemented)?;
                 self.inferred_obligations.extend(obligations);
 
                 // Construct the nested Field<T>: Unsize<Field<U>> predicate.
@@ -2696,15 +2685,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                skol_obligation_trait_ref);
 
         let InferOk { obligations, .. } =
-            self.infcx.eq_trait_refs(false,
-                                     &obligation.cause,
-                                     obligation.param_env,
-                                     impl_trait_ref.value.clone(),
-                                     skol_obligation_trait_ref)
-            .map_err(|e| {
-                debug!("match_impl: failed eq_trait_refs due to `{}`", e);
-                ()
-            })?;
+            self.infcx.at(&obligation.cause, obligation.param_env)
+                      .eq(skol_obligation_trait_ref, impl_trait_ref.value)
+                      .map_err(|e| {
+                          debug!("match_impl: failed eq_trait_refs due to `{}`", e);
+                          ()
+                      })?;
         self.inferred_obligations.extend(obligations);
 
         if let Err(e) = self.infcx.leak_check(false,
@@ -2770,13 +2756,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                obligation,
                poly_trait_ref);
 
-        self.infcx.sub_poly_trait_refs(false,
-                                       obligation.cause.clone(),
-                                       obligation.param_env,
-                                       poly_trait_ref,
-                                       obligation.predicate.to_poly_trait_ref())
-            .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
-            .map_err(|_| ())
+        self.infcx.at(&obligation.cause, obligation.param_env)
+                  .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref)
+                  .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
+                  .map_err(|_| ())
     }
 
     ///////////////////////////////////////////////////////////////////////////
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index 0e9bf93cbd2d1..689f06a359730 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -228,11 +228,8 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                                                        target_substs);
 
     // do the impls unify? If not, no specialization.
-    match infcx.eq_trait_refs(true,
-                              &ObligationCause::dummy(),
-                              param_env,
-                              source_trait_ref,
-                              target_trait_ref) {
+    match infcx.at(&ObligationCause::dummy(), param_env)
+               .eq(source_trait_ref, target_trait_ref) {
         Ok(InferOk { obligations: o, .. }) => {
             obligations.extend(o);
         }
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index fb162f354e991..da8e3b5a42ba9 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -353,18 +353,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         infer_ok.value
     }
 
-    fn sub_types(&mut self, sup: Ty<'tcx>, sub: Ty<'tcx>)
+    fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>)
                  -> infer::UnitResult<'tcx>
     {
-        self.infcx.sub_types(false, &self.misc(self.last_span), self.param_env, sup, sub)
-            .map(|ok| self.register_infer_ok_obligations(ok))
+        self.infcx.at(&self.misc(self.last_span), self.param_env)
+                  .sup(sup, sub)
+                  .map(|ok| self.register_infer_ok_obligations(ok))
     }
 
     fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>)
                 -> infer::UnitResult<'tcx>
     {
-        self.infcx.eq_types(false, &self.misc(span), self.param_env, a, b)
-            .map(|ok| self.register_infer_ok_obligations(ok))
+        self.infcx.at(&self.misc(span), self.param_env)
+                  .eq(b, a)
+                  .map(|ok| self.register_infer_ok_obligations(ok))
     }
 
     fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index b1b4e099626cf..30ac7b4bfb9be 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -64,7 +64,7 @@ use check::{Diverges, FnCtxt};
 
 use rustc::hir;
 use rustc::hir::def_id::DefId;
-use rustc::infer::{Coercion, InferResult, InferOk, TypeTrace};
+use rustc::infer::{Coercion, InferResult, InferOk};
 use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::traits::{self, ObligationCause, ObligationCauseCode};
 use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
@@ -135,11 +135,13 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
 
     fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
         self.commit_if_ok(|_| {
-            let trace = TypeTrace::types(&self.cause, false, a, b);
             if self.use_lub {
-                self.lub(false, trace, self.fcx.param_env, &a, &b)
+                self.at(&self.cause, self.fcx.param_env)
+                    .lub(b, a)
             } else {
-                self.sub(false, trace, self.fcx.param_env, &a, &b)
+                self.at(&self.cause, self.fcx.param_env)
+                    .sup(b, a)
+                    .map(|InferOk { value: (), obligations }| InferOk { value: a, obligations })
             }
         })
     }
@@ -771,20 +773,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             return Ok(prev_ty);
         }
 
-        let trace = TypeTrace::types(cause, true, prev_ty, new_ty);
-
         // Special-case that coercion alone cannot handle:
         // Two function item types of differing IDs or Substs.
         match (&prev_ty.sty, &new_ty.sty) {
             (&ty::TyFnDef(a_def_id, a_substs, a_fty), &ty::TyFnDef(b_def_id, b_substs, b_fty)) => {
                 // The signature must always match.
-                let fty = self.lub(true, trace.clone(), self.param_env, &a_fty, &b_fty)
+                let fty = self.at(cause, self.param_env)
+                              .trace(prev_ty, new_ty)
+                              .lub(&a_fty, &b_fty)
                               .map(|ok| self.register_infer_ok_obligations(ok))?;
 
                 if a_def_id == b_def_id {
                     // Same function, maybe the parameters match.
                     let substs = self.commit_if_ok(|_| {
-                        self.lub(true, trace.clone(), self.param_env, &a_substs, &b_substs)
+                        self.at(cause, self.param_env)
+                            .trace(prev_ty, new_ty)
+                            .lub(&a_substs, &b_substs)
                             .map(|ok| self.register_infer_ok_obligations(ok))
                     });
 
@@ -853,7 +857,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
             if !noop {
                 return self.commit_if_ok(|_| {
-                    self.lub(true, trace.clone(), self.param_env, &prev_ty, &new_ty)
+                    self.at(cause, self.param_env)
+                        .lub(prev_ty, new_ty)
                         .map(|ok| self.register_infer_ok_obligations(ok))
                 });
             }
@@ -866,7 +871,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     Err(e)
                 } else {
                     self.commit_if_ok(|_| {
-                        self.lub(true, trace, self.param_env, &prev_ty, &new_ty)
+                        self.at(cause, self.param_env)
+                            .lub(prev_ty, new_ty)
                             .map(|ok| self.register_infer_ok_obligations(ok))
                     })
                 }
@@ -1109,7 +1115,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
             // Another example is `break` with no argument expression.
             assert!(expression_ty.is_nil());
             assert!(expression_ty.is_nil(), "if let hack without unit type");
-            fcx.eq_types(label_expression_as_expected, cause, fcx.param_env, expression_ty, self.merged_ty())
+            fcx.at(cause, fcx.param_env)
+               .eq_exp(label_expression_as_expected, expression_ty, self.merged_ty())
                .map(|infer_ok| {
                    fcx.register_infer_ok_obligations(infer_ok);
                    expression_ty
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index 50e7e89a5f61a..7633be24477c6 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -289,7 +289,8 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         debug!("compare_impl_method: trait_fty={:?}", trait_fty);
 
-        let sub_result = infcx.sub_types(false, &cause, param_env, impl_fty, trait_fty)
+        let sub_result = infcx.at(&cause, param_env)
+                              .sup(trait_fty, impl_fty)
                               .map(|InferOk { obligations, .. }| {
                                   inh.register_predicates(obligations);
                               });
@@ -460,28 +461,23 @@ fn extract_spans_for_error_reporting<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a
                 impl_iter.zip(trait_iter)
                          .zip(impl_m_iter)
                          .zip(trait_m_iter)
-                         .filter_map(|(((impl_arg_ty, trait_arg_ty), impl_arg), trait_arg)| {
-                             match infcx.sub_types(true,
-                                                   &cause,
-                                                   param_env,
-                                                   trait_arg_ty,
-                                                   impl_arg_ty) {
+                         .filter_map(|(((&impl_arg_ty, &trait_arg_ty), impl_arg), trait_arg)| {
+                             match infcx.at(&cause, param_env).sub(trait_arg_ty, impl_arg_ty) {
                                  Ok(_) => None,
                                  Err(_) => Some((impl_arg.span, Some(trait_arg.span))),
                              }
                          })
                          .next()
                          .unwrap_or_else(|| {
-                             if infcx.sub_types(false,
-                                                &cause,
-                                                param_env,
-                                                impl_sig.output(),
-                                                trait_sig.output())
-                                     .is_err() {
-                                         (impl_m_output.span(), Some(trait_m_output.span()))
-                                     } else {
-                                         (cause.span, tcx.hir.span_if_local(trait_m.def_id))
-                                     }
+                             if
+                                 infcx.at(&cause, param_env)
+                                      .sup(trait_sig.output(), impl_sig.output())
+                                      .is_err()
+                             {
+                                 (impl_m_output.span(), Some(trait_m_output.span()))
+                             } else {
+                                 (cause.span, tcx.hir.span_if_local(trait_m.def_id))
+                             }
                          })
             } else {
                 (cause.span, tcx.hir.span_if_local(trait_m.def_id))
@@ -760,8 +756,9 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         debug!("compare_const_impl: trait_ty={:?}", trait_ty);
 
-        let err = infcx.sub_types(false, &cause, param_env, impl_ty, trait_ty)
-            .map(|ok| inh.register_infer_ok_obligations(ok));
+        let err = infcx.at(&cause, param_env)
+                       .sup(trait_ty, impl_ty)
+                       .map(|ok| inh.register_infer_ok_obligations(ok));
 
         if let Err(terr) = err {
             debug!("checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 9c3dcc3f0e0eb..40d53b5e97991 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -26,8 +26,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     // Requires that the two types unify, and prints an error message if
     // they don't.
     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
-        let cause = self.misc(sp);
-        match self.sub_types(false, &cause, self.param_env, actual, expected) {
+        let cause = &self.misc(sp);
+        match self.at(cause, self.param_env).sup(expected, actual) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
             },
@@ -54,7 +54,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                      cause: &ObligationCause<'tcx>,
                                      expected: Ty<'tcx>,
                                      actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
-        match self.eq_types(false, cause, self.param_env, actual, expected) {
+        match self.at(cause, self.param_env).eq(expected, actual) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
                 None
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 024d1f09f1cf7..bff9289de5055 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -92,7 +92,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
         let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs);
 
         let cause = &ObligationCause::misc(drop_impl_span, drop_impl_node_id);
-        match infcx.eq_types(true, cause, impl_param_env, named_type, fresh_impl_self_ty) {
+        match infcx.at(cause, impl_param_env).eq(named_type, fresh_impl_self_ty) {
             Ok(InferOk { obligations, .. }) => {
                 fulfillment_cx.register_predicate_obligations(infcx, obligations);
             }
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index a5cf4dd268304..c8815f3df5aa2 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -340,7 +340,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
     }
 
     fn unify_receivers(&mut self, self_ty: Ty<'tcx>, method_self_ty: Ty<'tcx>) {
-        match self.sub_types(false, &self.misc(self.span), self.param_env, self_ty, method_self_ty) {
+        match self.at(&self.misc(self.span), self.param_env).sup(method_self_ty, self_ty) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
             }
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 449c0f0281044..f975a83592362 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -679,7 +679,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                     let output = fty.output().subst(self.tcx, substs);
                     let (output, _) = self.replace_late_bound_regions_with_fresh_var(
                         self.span, infer::FnCall, &output);
-                    self.can_sub_types(self.param_env, output, expected).is_ok()
+                    self.can_sub(self.param_env, output, expected).is_ok()
                 })
             }
             _ => false,
@@ -885,7 +885,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                        substs,
                        bound);
 
-                if self.can_equate(self.param_env, &step.self_ty, &bound.self_ty()).is_ok() {
+                if self.can_eq(self.param_env, step.self_ty, bound.self_ty()).is_ok() {
                     let xform_self_ty = self.xform_self_ty(&item, bound.self_ty(), bound.substs);
 
                     debug!("assemble_projection_candidates: bound={:?} xform_self_ty={:?}",
@@ -1143,11 +1143,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
 
         self.probe(|_| {
             // First check that the self type can be related.
-            let sub_obligations = match self.sub_types(false,
-                                                       &ObligationCause::dummy(),
-                                                       self.param_env,
-                                                       self_ty,
-                                                       probe.xform_self_ty) {
+            let sub_obligations = match self.at(&ObligationCause::dummy(), self.param_env)
+                                            .sup(probe.xform_self_ty, self_ty) {
                 Ok(InferOk { obligations, value: () }) => obligations,
                 Err(_) => {
                     debug!("--> cannot relate self-types");
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 055ce94d20105..48afd91b23337 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -2723,7 +2723,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // is polymorphic) and the expected return type.
                 // No argument expectations are produced if unification fails.
                 let origin = self.misc(call_span);
-                let ures = self.sub_types(false, &origin, self.param_env, formal_ret, ret_ty);
+                let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret);
 
                 // FIXME(#15760) can't use try! here, FromError doesn't default
                 // to identity so the resulting type is not constrained.
@@ -4218,7 +4218,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             _ => return,
         };
         let last_expr_ty = self.expr_ty(last_expr);
-        if self.can_sub_types(self.param_env, last_expr_ty, expected_ty).is_err() {
+        if self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() {
             return;
         }
         let original_span = original_sp(last_stmt.span, blk.span);
@@ -4478,7 +4478,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             let ty = self.tcx.type_of(impl_def_id);
 
             let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
-            match self.sub_types(false, &self.misc(span), self.param_env, self_ty, impl_ty) {
+            match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {
                 Ok(ok) => self.register_infer_ok_obligations(ok),
                 Err(_) => {
                     span_bug!(span,
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index db817efe93d4a..d2475a2e5b2fa 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -1660,7 +1660,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
 
                     // check whether this predicate applies to our current projection
                     let cause = self.fcx.misc(span);
-                    match self.eq_types(false, &cause, self.fcx.param_env, ty, outlives.0) {
+                    match self.at(&cause, self.fcx.param_env).eq(outlives.0, ty) {
                         Ok(ok) => {
                             self.register_infer_ok_obligations(ok);
                             Ok(outlives.1)
diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs
index 8c2679f1bbf53..377b7b069d331 100644
--- a/src/librustc_typeck/coherence/builtin.rs
+++ b/src/librustc_typeck/coherence/builtin.rs
@@ -308,7 +308,7 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         // we may have to evaluate constraint
                         // expressions in the course of execution.)
                         // See e.g. #41936.
-                        if let Ok(ok) = infcx.eq_types(false, &cause, param_env, b, a) {
+                        if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) {
                             if ok.obligations.is_empty() {
                                 return None;
                             }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index ac0e360adb08a..06cb9f948c9b3 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -158,7 +158,7 @@ fn require_same_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     tcx.infer_ctxt(()).enter(|ref infcx| {
         let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
         let mut fulfill_cx = FulfillmentContext::new();
-        match infcx.eq_types(false, &cause, param_env, expected, actual) {
+        match infcx.at(&cause, param_env).eq(expected, actual) {
             Ok(InferOk { obligations, .. }) => {
                 fulfill_cx.register_predicate_obligations(infcx, obligations);
             }

From af223fae27d2aa16743abadeb5635f8452a49894 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 24 May 2017 12:01:53 -0400
Subject: [PATCH 6/8] pacify the mercilous tidy

---
 src/librustc/traits/fulfill.rs            | 12 ++++--
 src/librustc/traits/project.rs            |  5 ++-
 src/librustc/traits/select.rs             |  4 +-
 src/librustc/ty/layout.rs                 | 48 +++++++++++++----------
 src/librustc_typeck/check/method/probe.rs |  4 +-
 src/librustc_typeck/check/mod.rs          |  5 ++-
 src/librustc_typeck/check/regionck.rs     |  3 +-
 7 files changed, 52 insertions(+), 29 deletions(-)

diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index daeb96e188d37..c2fe04534375f 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -420,7 +420,9 @@ fn process_predicate<'a, 'gcx, 'tcx>(
         }
 
         ty::Predicate::Equate(ref binder) => {
-            match selcx.infcx().equality_predicate(&obligation.cause, obligation.param_env, binder) {
+            match selcx.infcx().equality_predicate(&obligation.cause,
+                                                   obligation.param_env,
+                                                   binder) {
                 Ok(InferOk { obligations, value: () }) => {
                     Ok(Some(obligations))
                 },
@@ -508,7 +510,9 @@ fn process_predicate<'a, 'gcx, 'tcx>(
         }
 
         ty::Predicate::WellFormed(ty) => {
-            match ty::wf::obligations(selcx.infcx(), obligation.param_env, obligation.cause.body_id,
+            match ty::wf::obligations(selcx.infcx(),
+                                      obligation.param_env,
+                                      obligation.cause.body_id,
                                       ty, obligation.cause.span) {
                 None => {
                     pending_obligation.stalled_on = vec![ty];
@@ -519,7 +523,9 @@ fn process_predicate<'a, 'gcx, 'tcx>(
         }
 
         ty::Predicate::Subtype(ref subtype) => {
-            match selcx.infcx().subtype_predicate(&obligation.cause, obligation.param_env, subtype) {
+            match selcx.infcx().subtype_predicate(&obligation.cause,
+                                                  obligation.param_env,
+                                                  subtype) {
                 None => {
                     // none means that both are unresolved
                     pending_obligation.stalled_on = vec![subtype.skip_binder().a,
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index dbeaf46e8be0d..787452121d375 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -500,7 +500,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
                    cacheable);
 
             let result = if projected_ty.has_projection_types() {
-                let mut normalizer = AssociatedTypeNormalizer::new(selcx, param_env, cause, depth+1);
+                let mut normalizer = AssociatedTypeNormalizer::new(selcx,
+                                                                   param_env,
+                                                                   cause,
+                                                                   depth+1);
                 let normalized_ty = normalizer.fold(&projected_ty);
 
                 debug!("opt_normalize_projection_type: \
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 6ac26e413ce4a..998201ad8d9ff 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -553,7 +553,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             }
 
             ty::Predicate::WellFormed(ty) => {
-                match ty::wf::obligations(self.infcx, obligation.param_env, obligation.cause.body_id,
+                match ty::wf::obligations(self.infcx,
+                                          obligation.param_env,
+                                          obligation.cause.body_id,
                                           ty, obligation.cause.span) {
                     Some(obligations) =>
                         self.evaluate_predicates_recursively(previous_stack, obligations.iter()),
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index c37fb0f5a778d..12af56d5c3dfa 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -1788,10 +1788,11 @@ impl<'a, 'tcx> Layout {
                 Fields::WithDiscrim(s) => (s, &s.offsets[1..]),
                 Fields::NoDiscrim(s) => (s, &s.offsets[0..]),
             };
-            let field_info: Vec<_> = flds.iter()
-                                         .zip(field_offsets.iter())
-                                         .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset))
-                                         .collect();
+            let field_info: Vec<_> =
+                flds.iter()
+                    .zip(field_offsets.iter())
+                    .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset))
+                    .collect();
 
             session::VariantInfo {
                 name: n.map(|n|n.to_string()),
@@ -1814,9 +1815,10 @@ impl<'a, 'tcx> Layout {
                 debug!("print-type-size t: `{:?}` adt struct-wrapped nullable nndiscr {} is {:?}",
                        ty, nndiscr, variant_layout);
                 let variant_def = &adt_def.variants[nndiscr as usize];
-                let fields: Vec<_> = variant_def.fields.iter()
-                                                       .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
-                                                       .collect();
+                let fields: Vec<_> =
+                    variant_def.fields.iter()
+                                      .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
+                                      .collect();
                 record(adt_kind.into(),
                        None,
                        vec![build_variant_info(Some(variant_def.name),
@@ -1840,9 +1842,10 @@ impl<'a, 'tcx> Layout {
                         "univariant with variants {:?}", variant_names());
                 if adt_def.variants.len() == 1 {
                     let variant_def = &adt_def.variants[0];
-                    let fields: Vec<_> = variant_def.fields.iter()
-                                                           .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
-                                                           .collect();
+                    let fields: Vec<_> =
+                        variant_def.fields.iter()
+                                          .map(|f| (f.name, f.ty(tcx, substs)))
+                                          .collect();
                     record(adt_kind.into(),
                            None,
                            vec![build_variant_info(Some(variant_def.name),
@@ -1858,17 +1861,20 @@ impl<'a, 'tcx> Layout {
             Layout::General { ref variants, discr, .. } => {
                 debug!("print-type-size t: `{:?}` adt general variants def {} layouts {} {:?}",
                        ty, adt_def.variants.len(), variants.len(), variants);
-                let variant_infos: Vec<_> = adt_def.variants.iter()
-                                                            .zip(variants.iter())
-                                                            .map(|(variant_def, variant_layout)| {
-                                                                let fields: Vec<_> = variant_def.fields.iter()
-                                                                                                       .map(|field_def| (field_def.name, field_def.ty(tcx, substs)))
-                                                                                                       .collect();
-                                                                build_variant_info(Some(variant_def.name),
-                                                                                   &fields,
-                                                                                   Fields::WithDiscrim(variant_layout))
-                                                            })
-                                                            .collect();
+                let variant_infos: Vec<_> =
+                    adt_def.variants.iter()
+                                    .zip(variants.iter())
+                                    .map(|(variant_def, variant_layout)| {
+                                        let fields: Vec<_> =
+                                            variant_def.fields
+                                                       .iter()
+                                                       .map(|f| (f.name, f.ty(tcx, substs)))
+                                                       .collect();
+                                        build_variant_info(Some(variant_def.name),
+                                                           &fields,
+                                                           Fields::WithDiscrim(variant_layout))
+                                    })
+                                    .collect();
                 record(adt_kind.into(), Some(discr.size()), variant_infos);
             }
 
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index f975a83592362..2518a1739f73e 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -1183,7 +1183,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                 traits::normalize(selcx, self.param_env, cause.clone(), &impl_bounds);
 
             // Convert the bounds into obligations.
-            let obligations = traits::predicates_for_generics(cause.clone(), self.param_env, &impl_bounds);
+            let obligations = traits::predicates_for_generics(cause.clone(),
+                                                              self.param_env,
+                                                              &impl_bounds);
             debug!("impl_obligations={:?}", obligations);
 
             // Evaluate those obligations to see if they might possibly hold.
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 48afd91b23337..ccaf9b84a45ab 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1908,7 +1908,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                                     -> InferOk<'tcx, T>
         where T : TypeFoldable<'tcx>
     {
-        self.inh.normalize_associated_types_in_as_infer_ok(span, self.body_id, self.param_env, value)
+        self.inh.normalize_associated_types_in_as_infer_ok(span,
+                                                           self.body_id,
+                                                           self.param_env,
+                                                           value)
     }
 
     pub fn write_nil(&self, node_id: ast::NodeId) {
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index d2475a2e5b2fa..00b044a9beff7 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -386,7 +386,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         for &ty in fn_sig_tys {
             let ty = self.resolve_type(ty);
             debug!("relate_free_regions(t={:?})", ty);
-            let implied_bounds = ty::wf::implied_bounds(self, self.fcx.param_env, body_id, ty, span);
+            let implied_bounds =
+                ty::wf::implied_bounds(self, self.fcx.param_env, body_id, ty, span);
 
             // Record any relations between free regions that we observe into the free-region-map.
             self.free_region_map.relate_free_regions_from_implied_bounds(&implied_bounds);

From 4c599bc198fd882331cf4721536b2ca8fe2893d2 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 24 May 2017 16:15:04 -0400
Subject: [PATCH 7/8] fix librustc_driver tests

---
 src/librustc_driver/test.rs | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 1d236a96bf62e..2b74d0a812b4a 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -46,6 +46,7 @@ use rustc::hir;
 struct Env<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     infcx: &'a infer::InferCtxt<'a, 'gcx, 'tcx>,
     region_maps: &'a mut RegionMaps,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 struct RH<'a> {
@@ -153,9 +154,13 @@ fn test_env<F>(source_string: &str,
                              index,
                              "test_crate",
                              |tcx| {
-        tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
+        tcx.infer_ctxt(()).enter(|infcx| {
             let mut region_maps = RegionMaps::new();
-            body(Env { infcx: &infcx, region_maps: &mut region_maps });
+            body(Env {
+                infcx: &infcx,
+                region_maps: &mut region_maps,
+                param_env: ty::ParamEnv::empty(Reveal::UserFacing),
+            });
             let free_regions = FreeRegionMap::new();
             let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID);
             infcx.resolve_regions_and_report_errors(def_id, &region_maps, &free_regions);
@@ -250,14 +255,14 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
     }
 
     pub fn make_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
-        match self.infcx.sub_types(true, &ObligationCause::dummy(), a, b) {
+        match self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(a, b) {
             Ok(_) => true,
             Err(ref e) => panic!("Encountered error: {}", e),
         }
     }
 
     pub fn is_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
-        self.infcx.can_sub_types(a, b).is_ok()
+        self.infcx.can_sub(self.param_env, a, b).is_ok()
     }
 
     pub fn assert_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) {
@@ -354,30 +359,23 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
                                   self.tcx().types.isize)
     }
 
-    pub fn dummy_type_trace(&self) -> infer::TypeTrace<'tcx> {
-        infer::TypeTrace::dummy(self.tcx())
-    }
-
-    pub fn sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
-        let trace = self.dummy_type_trace();
-        self.infcx.sub(true, trace, &t1, &t2)
+    pub fn sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, ()> {
+        self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(t1, t2)
     }
 
     pub fn lub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
-        let trace = self.dummy_type_trace();
-        self.infcx.lub(true, trace, &t1, &t2)
+        self.infcx.at(&ObligationCause::dummy(), self.param_env).lub(t1, t2)
     }
 
     pub fn glb(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
-        let trace = self.dummy_type_trace();
-        self.infcx.glb(true, trace, &t1, &t2)
+        self.infcx.at(&ObligationCause::dummy(), self.param_env).glb(t1, t2)
     }
 
     /// Checks that `t1 <: t2` is true (this may register additional
     /// region checks).
     pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) {
         match self.sub(t1, t2) {
-            Ok(InferOk { obligations, .. }) => {
+            Ok(InferOk { obligations, value: () }) => {
                 // None of these tests should require nested obligations:
                 assert!(obligations.is_empty());
             }

From 84047db2ade806b802f36d522ea82bd5d3cbb242 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Fri, 26 May 2017 19:20:54 -0400
Subject: [PATCH 8/8] fix ui reference

---
 src/test/ui/issue-26548.stderr | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/ui/issue-26548.stderr b/src/test/ui/issue-26548.stderr
index 8bfe4ac733b6d..c27ad7680a570 100644
--- a/src/test/ui/issue-26548.stderr
+++ b/src/test/ui/issue-26548.stderr
@@ -5,5 +5,5 @@ note: ...which then requires computing layout of `std::option::Option<<S as Mirr
 note: ...which then requires computing layout of `<S as Mirror>::It`...
   = note: ...which then again requires computing layout of `S`, completing the cycle.
 
-error: aborting due to previous error
+error: aborting due to previous error(s)