From 9ae5db20d2b659a78ef3f33d8a10f418cc3b8e1c Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 15 Jul 2017 06:41:19 -0400
Subject: [PATCH 01/63] introduce `Universe` struct

---
 src/librustc/ty/mod.rs | 62 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 0deababd21829..f137d1b57e5f9 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1236,6 +1236,68 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
     }
 }
 
+/// "Universes" are used during type- and trait-checking in the
+/// presence of `for<..>` binders to control what sets of names are
+/// visible. Universes are arranged into a tree: the root universe
+/// contains names that are always visible. But when you enter into
+/// some subuniverse, then it may add names that are only visible
+/// within that subtree (but it can still name the names of its
+/// ancestor universes).
+///
+/// To make this more concrete, consider this program:
+///
+/// ```
+/// struct Foo { }
+/// fn bar<T>(x: T) {
+///   let y: for<'a> fn(&'a u8, Foo) = ...;
+/// }
+/// ```
+///
+/// The struct name `Foo` is in the root universe U0. But the type
+/// parameter `T`, introduced on `bar`, is in a subuniverse U1 --
+/// i.e., within `bar`, we can name both `T` and `Foo`, but outside of
+/// `bar`, we cannot name `T`. Then, within the type of `y`, the
+/// region `'a` is in a subuniverse U2 of U1, because we can name it
+/// inside the fn type but not outside.
+///
+/// Universes are related to **skolemization** -- which is a way of
+/// doing type- and trait-checking around these "forall" binders (also
+/// called **universal quantification**). The idea is that when, in
+/// the body of `bar`, we refer to `T` as a type, we aren't referring
+/// to any type in particular, but rather a kind of "fresh" type that
+/// is distinct from all other types we have actually declared. This
+/// is called a **skolemized** type, and we use universes to talk
+/// about this. In other words, a type name in universe 0 always
+/// corresponds to some "ground" type that the user declared, but a
+/// type name in a non-zero universe is a skolemized type -- an
+/// idealized representative of "types in general" that we use for
+/// checking generic functions.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct UniverseIndex(u32);
+
+impl UniverseIndex {
+    /// The root universe, where things that the user defined are
+    /// visible.
+    pub fn root() -> UniverseIndex {
+        UniverseIndex(0)
+    }
+
+    /// A "subuniverse" corresponds to being inside a `forall` quantifier.
+    /// So, for example, suppose we have this type in universe `U`:
+    ///
+    /// ```
+    /// for<'a> fn(&'a u32)
+    /// ```
+    ///
+    /// Once we "enter" into this `for<'a>` quantifier, we are in a
+    /// subuniverse of `U` -- in this new universe, we can name the
+    /// region `'a`, but that region was not nameable from `U` because
+    /// it was not in scope there.
+    pub fn subuniverse(self) -> UniverseIndex {
+        UniverseIndex(self.0 + 1)
+    }
+}
+
 /// When type checking, we use the `ParamEnv` to track
 /// details about the set of where-clauses that are in scope at this
 /// particular point.

From e88898fe127b3e9cbe409ac5e425e664088f2ae9 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 15 Jul 2017 06:47:30 -0400
Subject: [PATCH 02/63] add some comments to `Obligation`

---
 src/librustc/traits/mod.rs | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index c08fe187f99bf..12b39cd876a5f 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -68,10 +68,21 @@ mod util;
 /// scope. The eventual result is usually a `Selection` (defined below).
 #[derive(Clone, PartialEq, Eq)]
 pub struct Obligation<'tcx, T> {
+    /// Why do we have to prove this thing?
     pub cause: ObligationCause<'tcx>,
+
+    /// In which environment should we prove this thing?
     pub param_env: ty::ParamEnv<'tcx>,
-    pub recursion_depth: usize,
+
+    /// What are we trying to prove?
     pub predicate: T,
+
+    /// If we started proving this as a result of trying to prove
+    /// something else, track the total depth to ensure termination.
+    /// If this goes over a certain threshold, we abort compilation --
+    /// in such cases, we can not say whether or not the predicate
+    /// holds for certain. Stupid halting problem. Such a drag.
+    pub recursion_depth: usize,
 }
 
 pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;

From 1da0b70c57db54c2b5836de5a8053fbf53229d29 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 15 Jul 2017 07:23:28 -0400
Subject: [PATCH 03/63] introduce `UniverseIndex` into `ParamEnv`

Always using root environment for now.
---
 src/librustc/ich/impls_ty.rs                | 10 +++++++++
 src/librustc/traits/mod.rs                  |  7 ++++--
 src/librustc/ty/mod.rs                      | 25 +++++++++++++++++----
 src/librustc/ty/structural_impls.rs         | 16 +++++++++++--
 src/librustc/ty/util.rs                     |  7 +++---
 src/librustc_typeck/check/compare_method.rs |  3 ++-
 6 files changed, 56 insertions(+), 12 deletions(-)

diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index e7627b110fae4..c8000968e61e4 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -801,6 +801,7 @@ for ty::steal::Steal<T>
 
 impl_stable_hash_for!(struct ty::ParamEnv<'tcx> {
     caller_bounds,
+    universe,
     reveal
 });
 
@@ -970,3 +971,12 @@ for traits::VtableGeneratorData<'gcx, N> where N: HashStable<StableHashingContex
         nested.hash_stable(hcx, hasher);
     }
 }
+
+impl<'gcx> HashStable<StableHashingContext<'gcx>>
+for ty::UniverseIndex {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'gcx>,
+                                          hasher: &mut StableHasher<W>) {
+        self.depth().hash_stable(hcx, hasher);
+    }
+}
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 12b39cd876a5f..09c7d7316baa2 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -520,7 +520,8 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
            predicates);
 
     let elaborated_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
-                                           unnormalized_env.reveal);
+                                           unnormalized_env.reveal,
+                                           unnormalized_env.universe);
 
     tcx.infer_ctxt().enter(|infcx| {
         let predicates = match fully_normalize(
@@ -573,7 +574,9 @@ 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), unnormalized_env.reveal)
+        ty::ParamEnv::new(tcx.intern_predicates(&predicates),
+                          unnormalized_env.reveal,
+                          unnormalized_env.universe)
     })
 }
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index f137d1b57e5f9..bfd7681644a4a 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1278,9 +1278,7 @@ pub struct UniverseIndex(u32);
 impl UniverseIndex {
     /// The root universe, where things that the user defined are
     /// visible.
-    pub fn root() -> UniverseIndex {
-        UniverseIndex(0)
-    }
+    pub const ROOT: UniverseIndex = UniverseIndex(0);
 
     /// A "subuniverse" corresponds to being inside a `forall` quantifier.
     /// So, for example, suppose we have this type in universe `U`:
@@ -1296,6 +1294,13 @@ impl UniverseIndex {
     pub fn subuniverse(self) -> UniverseIndex {
         UniverseIndex(self.0 + 1)
     }
+
+    /// Gets the "depth" of this universe in the universe tree. This
+    /// is not really useful except for e.g. the `HashStable`
+    /// implementation
+    pub fn depth(&self) -> u32 {
+        self.0
+    }
 }
 
 /// When type checking, we use the `ParamEnv` to track
@@ -1312,6 +1317,17 @@ pub struct ParamEnv<'tcx> {
     /// want `Reveal::All` -- note that this is always paired with an
     /// empty environment. To get that, use `ParamEnv::reveal()`.
     pub reveal: traits::Reveal,
+
+    /// What is the innermost universe we have created? Starts out as
+    /// `UniverseIndex::root()` but grows from there as we enter
+    /// universal quantifiers.
+    ///
+    /// NB: At present, we exclude the universal quantifiers on the
+    /// item we are type-checking, and just consider those names as
+    /// part of the root universe. So this would only get incremented
+    /// when we enter into a higher-ranked (`for<..>`) type or trait
+    /// bound.
+    pub universe: UniverseIndex,
 }
 
 impl<'tcx> ParamEnv<'tcx> {
@@ -2627,7 +2643,8 @@ fn param_env<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // sure that this will succeed without errors anyway.
 
     let unnormalized_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates),
-                                             traits::Reveal::UserFacing);
+                                             traits::Reveal::UserFacing,
+                                             ty::UniverseIndex::ROOT);
 
     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/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 5f1448cd1f18e..e9da33215a985 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -239,6 +239,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> {
         tcx.lift(&self.caller_bounds).map(|caller_bounds| {
             ty::ParamEnv {
                 reveal: self.reveal,
+                universe: self.universe,
                 caller_bounds,
             }
         })
@@ -601,12 +602,23 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ParamEnv<'tcx> {
         ty::ParamEnv {
             reveal: self.reveal,
             caller_bounds: self.caller_bounds.fold_with(folder),
+            universe: self.universe.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)
+        let &ty::ParamEnv { reveal: _, ref universe, ref caller_bounds } = self;
+        universe.super_visit_with(visitor) || caller_bounds.super_visit_with(visitor)
+    }
+}
+
+impl<'tcx> TypeFoldable<'tcx> for ty::UniverseIndex {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _folder: &mut F) -> Self {
+        *self
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> bool {
+        false
     }
 }
 
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 39842a543b54b..2a7b0fb20e9fa 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -153,14 +153,15 @@ impl<'tcx> ty::ParamEnv<'tcx> {
     /// Construct a trait environment suitable for contexts where
     /// there are no where clauses in scope.
     pub fn empty(reveal: Reveal) -> Self {
-        Self::new(ty::Slice::empty(), reveal)
+        Self::new(ty::Slice::empty(), reveal, ty::UniverseIndex::ROOT)
     }
 
     /// Construct a trait environment with the given set of predicates.
     pub fn new(caller_bounds: &'tcx ty::Slice<ty::Predicate<'tcx>>,
-               reveal: Reveal)
+               reveal: Reveal,
+               universe: ty::UniverseIndex)
                -> Self {
-        ty::ParamEnv { caller_bounds, reveal }
+        ty::ParamEnv { caller_bounds, reveal, universe }
     }
 
     /// Returns a new parameter environment with the same clauses, but
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index 554a858bcc173..d6c0156cbfec2 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -213,7 +213,8 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // 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),
-                                      Reveal::UserFacing);
+                                      Reveal::UserFacing,
+                                      ty::UniverseIndex::ROOT);
     let param_env = traits::normalize_param_env_or_error(tcx,
                                                          impl_m.def_id,
                                                          param_env,

From 55f33ba3b8dd353cc31ea37b3b5c2f73c55a443b Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 15 Jul 2017 14:47:49 -0400
Subject: [PATCH 04/63] use `{}` for `Known` variant just for more parity

---
 src/librustc/infer/type_variable.rs | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index cc91a637b8931..5b575115835ac 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -76,7 +76,9 @@ struct TypeVariableData<'tcx> {
 }
 
 enum TypeVariableValue<'tcx> {
-    Known(Ty<'tcx>),
+    Known {
+        value: Ty<'tcx>
+    },
     Bounded {
         default: Option<Default<'tcx>>
     }
@@ -117,7 +119,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
 
     pub fn default(&self, vid: ty::TyVid) -> Option<Default<'tcx>> {
         match &self.values.get(vid.index as usize).value {
-            &Known(_) => None,
+            &Known { .. } => None,
             &Bounded { ref default, .. } => default.clone()
         }
     }
@@ -158,14 +160,14 @@ impl<'tcx> TypeVariableTable<'tcx> {
 
         let old_value = {
             let vid_data = &mut self.values[vid.index as usize];
-            mem::replace(&mut vid_data.value, TypeVariableValue::Known(ty))
+            mem::replace(&mut vid_data.value, TypeVariableValue::Known { value: ty })
         };
 
         match old_value {
             TypeVariableValue::Bounded { default } => {
                 self.values.record(Instantiate { vid: vid, default: default });
             }
-            TypeVariableValue::Known(old_ty) => {
+            TypeVariableValue::Known { value: old_ty } => {
                 bug!("instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
                      vid, ty, old_ty)
             }
@@ -233,7 +235,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
         debug_assert!(self.root_var(vid) == vid);
         match self.values.get(vid.index as usize).value {
             Bounded { .. } => None,
-            Known(t) => Some(t)
+            Known { value } => Some(value)
         }
     }
 
@@ -334,7 +336,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
                         // created since the snapshot started or not.
                         let escaping_type = match self.values.get(vid.index as usize).value {
                             Bounded { .. } => bug!(),
-                            Known(ty) => ty,
+                            Known { value } => value,
                         };
                         escaping_types.push(escaping_type);
                     }

From c5eb255ab46d4df0f567893cf4ff218a5588870d Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 15 Jul 2017 14:52:32 -0400
Subject: [PATCH 05/63] make `Default` Copy and Clone

---
 src/librustc/infer/type_variable.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index 5b575115835ac..12641e718af1f 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -86,7 +86,7 @@ enum TypeVariableValue<'tcx> {
 
 // We will use this to store the required information to recapitulate what happened when
 // an error occurs.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub struct Default<'tcx> {
     pub ty: Ty<'tcx>,
     /// The span where the default was incurred
@@ -120,7 +120,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
     pub fn default(&self, vid: ty::TyVid) -> Option<Default<'tcx>> {
         match &self.values.get(vid.index as usize).value {
             &Known { .. } => None,
-            &Bounded { ref default, .. } => default.clone()
+            &Bounded { default, .. } => default,
         }
     }
 
@@ -182,7 +182,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
         self.eq_relations.new_key(());
         self.sub_relations.new_key(());
         let index = self.values.push(TypeVariableData {
-            value: Bounded { default: default },
+            value: Bounded { default },
             origin,
             diverging,
         });

From 5c69168b5ee1237bc50f7e38b79ee773accef62c Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 15 Jul 2017 18:24:51 -0400
Subject: [PATCH 06/63] kill custom type inference defaults (these don't really
 work anyway)

---
 src/librustc/infer/mod.rs                   | 23 +++++----------------
 src/librustc_typeck/astconv.rs              |  5 ++---
 src/librustc_typeck/check/method/confirm.rs |  4 ++--
 src/librustc_typeck/check/method/mod.rs     |  4 ++--
 src/librustc_typeck/check/method/probe.rs   |  4 ++--
 src/librustc_typeck/check/mod.rs            |  9 ++++----
 6 files changed, 17 insertions(+), 32 deletions(-)

diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 79eeebfb25031..57d1312d0f124 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -23,7 +23,7 @@ use middle::free_region::{FreeRegionMap, RegionRelations};
 use middle::region;
 use middle::lang_items;
 use mir::tcx::LvalueTy;
-use ty::subst::{Kind, Subst, Substs};
+use ty::subst::{Substs};
 use ty::{TyVid, IntVid, FloatVid};
 use ty::{self, Ty, TyCtxt};
 use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
@@ -1003,26 +1003,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// as the substitutions for the default, `(T, U)`.
     pub fn type_var_for_def(&self,
                             span: Span,
-                            def: &ty::TypeParameterDef,
-                            substs: &[Kind<'tcx>])
+                            def: &ty::TypeParameterDef)
                             -> Ty<'tcx> {
-        let default = if def.has_default {
-            let default = self.tcx.type_of(def.def_id);
-            Some(type_variable::Default {
-                ty: default.subst_spanned(self.tcx, substs, Some(span)),
-                origin_span: span,
-                def_id: def.def_id
-            })
-        } else {
-            None
-        };
-
-
         let ty_var_id = self.type_variables
                             .borrow_mut()
                             .new_var(false,
                                      TypeVariableOrigin::TypeParameterDefinition(span, def.name),
-                                     default);
+                                     None);
 
         self.tcx.mk_var(ty_var_id)
     }
@@ -1035,8 +1022,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                  -> &'tcx Substs<'tcx> {
         Substs::for_item(self.tcx, def_id, |def, _| {
             self.region_var_for_def(span, def)
-        }, |def, substs| {
-            self.type_var_for_def(span, def, substs)
+        }, |def, _| {
+            self.type_var_for_def(span, def)
         })
     }
 
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 1471e235156eb..bbde41a3f4d83 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -19,7 +19,7 @@ use hir::def::Def;
 use hir::def_id::DefId;
 use middle::resolve_lifetime as rl;
 use namespace::Namespace;
-use rustc::ty::subst::{Kind, Subst, Substs};
+use rustc::ty::subst::{Subst, Substs};
 use rustc::traits;
 use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
 use rustc::ty::wf::object_region_bounds;
@@ -51,7 +51,6 @@ pub trait AstConv<'gcx, 'tcx> {
     /// Same as ty_infer, but with a known type parameter definition.
     fn ty_infer_for_def(&self,
                         _def: &ty::TypeParameterDef,
-                        _substs: &[Kind<'tcx>],
                         span: Span) -> Ty<'tcx> {
         self.ty_infer(span)
     }
@@ -250,7 +249,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
             } else if infer_types {
                 // No type parameters were provided, we can infer all.
                 let ty_var = if !default_needs_object_self(def) {
-                    self.ty_infer_for_def(def, substs, span)
+                    self.ty_infer_for_def(def, span)
                 } else {
                     self.ty_infer(span)
                 };
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index a9830dd5ddece..5f303a055493d 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -305,7 +305,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
             } else {
                 self.region_var_for_def(self.span, def)
             }
-        }, |def, cur_substs| {
+        }, |def, _cur_substs| {
             let i = def.index as usize;
             if i < parent_substs.len() {
                 parent_substs.type_at(i)
@@ -316,7 +316,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
             {
                 self.to_ty(ast_ty)
             } else {
-                self.type_var_for_def(self.span, def, cur_substs)
+                self.type_var_for_def(self.span, def)
             }
         })
     }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 58d72e37d51cf..4a122fbc4c195 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -249,13 +249,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let substs = Substs::for_item(self.tcx,
                                       trait_def_id,
                                       |def, _| self.region_var_for_def(span, def),
-                                      |def, substs| {
+                                      |def, _substs| {
             if def.index == 0 {
                 self_ty
             } else if let Some(ref input_types) = opt_input_types {
                 input_types[def.index as usize - 1]
             } else {
-                self.type_var_for_def(span, def, substs)
+                self.type_var_for_def(span, def)
             }
         });
 
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 81e5b2fe00a6a..ce3c617f43444 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -1265,12 +1265,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                     // `impl_self_ty()` for an explanation.
                     self.tcx.types.re_erased
                 }
-            }, |def, cur_substs| {
+            }, |def, _cur_substs| {
                 let i = def.index as usize;
                 if i < substs.len() {
                     substs.type_at(i)
                 } else {
-                    self.type_var_for_def(self.span, def, cur_substs)
+                    self.type_var_for_def(self.span, def)
                 }
             });
             xform_fn_sig.subst(self.tcx, substs)
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 82d59ecfc92cf..71899a573801e 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -92,7 +92,7 @@ use namespace::Namespace;
 use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
 use rustc::infer::type_variable::{TypeVariableOrigin};
 use rustc::middle::region;
-use rustc::ty::subst::{Kind, Subst, Substs};
+use rustc::ty::subst::{Subst, Substs};
 use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
 use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
 use rustc::ty::{self, Ty, TyCtxt, Visibility};
@@ -1652,9 +1652,8 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> {
 
     fn ty_infer_for_def(&self,
                         ty_param_def: &ty::TypeParameterDef,
-                        substs: &[Kind<'tcx>],
                         span: Span) -> Ty<'tcx> {
-        self.type_var_for_def(span, ty_param_def, substs)
+        self.type_var_for_def(span, ty_param_def)
     }
 
     fn projected_ty_from_poly_trait_ref(&self,
@@ -4747,7 +4746,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // Handle Self first, so we can adjust the index to match the AST.
                 if has_self && i == 0 {
                     return opt_self_ty.unwrap_or_else(|| {
-                        self.type_var_for_def(span, def, substs)
+                        self.type_var_for_def(span, def)
                     });
                 }
                 i -= has_self as usize;
@@ -4780,7 +4779,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // This can also be reached in some error cases:
                 // We prefer to use inference variables instead of
                 // TyError to let type inference recover somewhat.
-                self.type_var_for_def(span, def, substs)
+                self.type_var_for_def(span, def)
             }
         });
 

From 3251227554d706f2d8ab08079002475827c92bcd Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 16 Jul 2017 04:55:48 -0400
Subject: [PATCH 07/63] kill supporting code from type-variable defaults

This was all unused anyway.
---
 src/librustc/infer/combine.rs       |  2 +-
 src/librustc/infer/mod.rs           | 43 ++-----------------------
 src/librustc/infer/type_variable.rs | 50 ++++++++---------------------
 src/librustc/ty/error.rs            | 45 +-------------------------
 src/librustc/ty/structural_impls.rs | 33 -------------------
 5 files changed, 17 insertions(+), 156 deletions(-)

diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 40e933b26a257..b1e3035c1abb9 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -410,7 +410,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                             }
 
                             let origin = variables.origin(vid);
-                            let new_var_id = variables.new_var(false, origin, None);
+                            let new_var_id = variables.new_var(false, origin);
                             let u = self.tcx().mk_var(new_var_id);
                             debug!("generalize: replacing original vid={:?} with new={:?}",
                                    vid, u);
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 57d1312d0f124..77a617dd78de4 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -628,22 +628,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    /// Returns a type variable's default fallback if any exists. A default
-    /// must be attached to the variable when created, if it is created
-    /// without a default, this will return None.
-    ///
-    /// This code does not apply to integral or floating point variables,
-    /// only to use declared defaults.
-    ///
-    /// See `new_ty_var_with_default` to create a type variable with a default.
-    /// See `type_variable::Default` for details about what a default entails.
-    pub fn default(&self, ty: Ty<'tcx>) -> Option<type_variable::Default<'tcx>> {
-        match ty.sty {
-            ty::TyInfer(ty::TyVar(vid)) => self.type_variables.borrow().default(vid),
-            _ => None
-        }
-    }
-
     pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> {
         let mut variables = Vec::new();
 
@@ -956,7 +940,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
         self.type_variables
             .borrow_mut()
-            .new_var(diverging, origin, None)
+            .new_var(diverging, origin)
     }
 
     pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
@@ -1008,8 +992,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         let ty_var_id = self.type_variables
                             .borrow_mut()
                             .new_var(false,
-                                     TypeVariableOrigin::TypeParameterDefinition(span, def.name),
-                                     None);
+                                     TypeVariableOrigin::TypeParameterDefinition(span, def.name));
 
         self.tcx.mk_var(ty_var_id)
     }
@@ -1220,28 +1203,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.report_and_explain_type_error(trace, &err)
     }
 
-    pub fn report_conflicting_default_types(&self,
-                                            span: Span,
-                                            body_id: ast::NodeId,
-                                            expected: type_variable::Default<'tcx>,
-                                            actual: type_variable::Default<'tcx>) {
-        let trace = TypeTrace {
-            cause: ObligationCause::misc(span, body_id),
-            values: Types(ExpectedFound {
-                expected: expected.ty,
-                found: actual.ty
-            })
-        };
-
-        self.report_and_explain_type_error(
-            trace,
-            &TypeError::TyParamDefaultMismatch(ExpectedFound {
-                expected,
-                found: actual
-            }))
-            .emit();
-    }
-
     pub fn replace_late_bound_regions_with_fresh_var<T>(
         &self,
         span: Span,
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index 12641e718af1f..209c19601f5e4 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 use self::TypeVariableValue::*;
-use hir::def_id::{DefId};
 use syntax::ast;
 use syntax_pos::Span;
 use ty::{self, Ty};
@@ -79,20 +78,7 @@ enum TypeVariableValue<'tcx> {
     Known {
         value: Ty<'tcx>
     },
-    Bounded {
-        default: Option<Default<'tcx>>
-    }
-}
-
-// We will use this to store the required information to recapitulate what happened when
-// an error occurs.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub struct Default<'tcx> {
-    pub ty: Ty<'tcx>,
-    /// The span where the default was incurred
-    pub origin_span: Span,
-    /// The definition that the default originates from
-    pub def_id: DefId
+    Unknown,
 }
 
 pub struct Snapshot {
@@ -101,9 +87,8 @@ pub struct Snapshot {
     sub_snapshot: ut::Snapshot<ty::TyVid>,
 }
 
-struct Instantiate<'tcx> {
+struct Instantiate {
     vid: ty::TyVid,
-    default: Option<Default<'tcx>>,
 }
 
 struct Delegate<'tcx>(PhantomData<&'tcx ()>);
@@ -117,13 +102,6 @@ impl<'tcx> TypeVariableTable<'tcx> {
         }
     }
 
-    pub fn default(&self, vid: ty::TyVid) -> Option<Default<'tcx>> {
-        match &self.values.get(vid.index as usize).value {
-            &Known { .. } => None,
-            &Bounded { default, .. } => default,
-        }
-    }
-
     pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool {
         self.values.get(vid.index as usize).diverging
     }
@@ -164,8 +142,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
         };
 
         match old_value {
-            TypeVariableValue::Bounded { default } => {
-                self.values.record(Instantiate { vid: vid, default: default });
+            TypeVariableValue::Unknown => {
+                self.values.record(Instantiate { vid: vid });
             }
             TypeVariableValue::Known { value: old_ty } => {
                 bug!("instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
@@ -176,13 +154,13 @@ impl<'tcx> TypeVariableTable<'tcx> {
 
     pub fn new_var(&mut self,
                    diverging: bool,
-                   origin: TypeVariableOrigin,
-                   default: Option<Default<'tcx>>,) -> ty::TyVid {
+                   origin: TypeVariableOrigin)
+                   -> ty::TyVid {
         debug!("new_var(diverging={:?}, origin={:?})", diverging, origin);
         self.eq_relations.new_key(());
         self.sub_relations.new_key(());
         let index = self.values.push(TypeVariableData {
-            value: Bounded { default },
+            value: Unknown,
             origin,
             diverging,
         });
@@ -234,7 +212,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
     pub fn probe_root(&mut self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
         debug_assert!(self.root_var(vid) == vid);
         match self.values.get(vid.index as usize).value {
-            Bounded { .. } => None,
+            Unknown => None,
             Known { value } => Some(value)
         }
     }
@@ -335,7 +313,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
                         // quick check to see if this variable was
                         // created since the snapshot started or not.
                         let escaping_type = match self.values.get(vid.index as usize).value {
-                            Bounded { .. } => bug!(),
+                            Unknown => bug!(),
                             Known { value } => value,
                         };
                         escaping_types.push(escaping_type);
@@ -366,12 +344,10 @@ impl<'tcx> TypeVariableTable<'tcx> {
 
 impl<'tcx> sv::SnapshotVecDelegate for Delegate<'tcx> {
     type Value = TypeVariableData<'tcx>;
-    type Undo = Instantiate<'tcx>;
+    type Undo = Instantiate;
 
-    fn reverse(values: &mut Vec<TypeVariableData<'tcx>>, action: Instantiate<'tcx>) {
-        let Instantiate { vid, default } = action;
-        values[vid.index as usize].value = Bounded {
-            default,
-        };
+    fn reverse(values: &mut Vec<TypeVariableData<'tcx>>, action: Instantiate) {
+        let Instantiate { vid } = action;
+        values[vid.index as usize].value = Unknown;
     }
 }
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs
index 5cfa72c07126f..7b46525544bfd 100644
--- a/src/librustc/ty/error.rs
+++ b/src/librustc/ty/error.rs
@@ -9,9 +9,8 @@
 // except according to those terms.
 
 use hir::def_id::DefId;
-use infer::type_variable;
 use middle::const_val::ConstVal;
-use ty::{self, BoundRegion, DefIdTree, Region, Ty, TyCtxt};
+use ty::{self, BoundRegion, Region, Ty, TyCtxt};
 
 use std::fmt;
 use syntax::abi;
@@ -52,7 +51,6 @@ pub enum TypeError<'tcx> {
     CyclicTy,
     ProjectionMismatched(ExpectedFound<DefId>),
     ProjectionBoundsLength(ExpectedFound<usize>),
-    TyParamDefaultMismatch(ExpectedFound<type_variable::Default<'tcx>>),
     ExistentialMismatch(ExpectedFound<&'tcx ty::Slice<ty::ExistentialPredicate<'tcx>>>),
 }
 
@@ -161,11 +159,6 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
                        values.expected,
                        values.found)
             },
-            TyParamDefaultMismatch(ref values) => {
-                write!(f, "conflicting type parameter defaults `{}` and `{}`",
-                       values.expected.ty,
-                       values.found.ty)
-            }
             ExistentialMismatch(ref values) => {
                 report_maybe_different(f, format!("trait `{}`", values.expected),
                                        format!("trait `{}`", values.found))
@@ -257,42 +250,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                         "consider boxing your closure and/or using it as a trait object");
                 }
             },
-            TyParamDefaultMismatch(values) => {
-                let expected = values.expected;
-                let found = values.found;
-                db.span_note(sp, &format!("conflicting type parameter defaults `{}` and `{}`",
-                                          expected.ty,
-                                          found.ty));
-
-                match self.hir.span_if_local(expected.def_id) {
-                    Some(span) => {
-                        db.span_note(span, "a default was defined here...");
-                    }
-                    None => {
-                        let item_def_id = self.parent(expected.def_id).unwrap();
-                        db.note(&format!("a default is defined on `{}`",
-                                         self.item_path_str(item_def_id)));
-                    }
-                }
-
-                db.span_note(
-                    expected.origin_span,
-                    "...that was applied to an unconstrained type variable here");
-
-                match self.hir.span_if_local(found.def_id) {
-                    Some(span) => {
-                        db.span_note(span, "a second default was defined here...");
-                    }
-                    None => {
-                        let item_def_id = self.parent(found.def_id).unwrap();
-                        db.note(&format!("a second default is defined on `{}`",
-                                         self.item_path_str(item_def_id)));
-                    }
-                }
-
-                db.span_note(found.origin_span,
-                             "...that also applies to the same type variable here");
-            }
             _ => {}
         }
     }
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index e9da33215a985..15257fc5a277e 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use infer::type_variable;
 use middle::const_val::{self, ConstVal, ConstAggregate, ConstEvalErr};
 use ty::{self, Lift, Ty, TyCtxt};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
@@ -382,19 +381,6 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::error::ExpectedFound<T> {
     }
 }
 
-impl<'a, 'tcx> Lift<'tcx> for type_variable::Default<'a> {
-    type Lifted = type_variable::Default<'tcx>;
-    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
-        tcx.lift(&self.ty).map(|ty| {
-            type_variable::Default {
-                ty,
-                origin_span: self.origin_span,
-                def_id: self.def_id
-            }
-        })
-    }
-}
-
 impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
     type Lifted = ty::error::TypeError<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@@ -426,9 +412,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
 
             Sorts(ref x) => return tcx.lift(x).map(Sorts),
-            TyParamDefaultMismatch(ref x) => {
-                return tcx.lift(x).map(TyParamDefaultMismatch)
-            }
             ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch)
         })
     }
@@ -1131,20 +1114,6 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::error::ExpectedFoun
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for type_variable::Default<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        type_variable::Default {
-            ty: self.ty.fold_with(folder),
-            origin_span: self.origin_span,
-            def_id: self.def_id
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.ty.visit_with(visitor)
-    }
-}
-
 impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec<I, T> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         self.iter().map(|x| x.fold_with(folder)).collect()
@@ -1184,7 +1153,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
             ProjectionMismatched(x) => ProjectionMismatched(x),
             ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
             Sorts(x) => Sorts(x.fold_with(folder)),
-            TyParamDefaultMismatch(ref x) => TyParamDefaultMismatch(x.fold_with(folder)),
             ExistentialMismatch(x) => ExistentialMismatch(x.fold_with(folder)),
         }
     }
@@ -1203,7 +1171,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
                 b.visit_with(visitor)
             },
             Sorts(x) => x.visit_with(visitor),
-            TyParamDefaultMismatch(ref x) => x.visit_with(visitor),
             ExistentialMismatch(x) => x.visit_with(visitor),
             Mismatch |
             Mutability |

From 744f10223414469a28a4730f831021ed6ec9dea8 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 16 Jul 2017 07:07:51 -0400
Subject: [PATCH 08/63] obtain `UnificationTable` and `snapshot_vec` from `ena`
 instead

The ena version has an improved interface. I suspect
`librustc_data_structures` should start migrating out to crates.io in
general.
---
 src/Cargo.lock                             | 10 ++++
 src/librustc/infer/combine.rs              |  8 +--
 src/librustc/infer/freshen.rs              |  4 +-
 src/librustc/infer/mod.rs                  | 65 ++++++++++++----------
 src/librustc/infer/region_inference/mod.rs | 10 ++--
 src/librustc/infer/type_variable.rs        | 15 +++--
 src/librustc/infer/unify_key.rs            | 44 +++++++--------
 src/librustc/ty/mod.rs                     |  5 +-
 src/librustc/util/ppaux.rs                 |  6 ++
 src/librustc_data_structures/Cargo.toml    |  1 +
 src/librustc_data_structures/lib.rs        |  5 +-
 11 files changed, 103 insertions(+), 70 deletions(-)

diff --git a/src/Cargo.lock b/src/Cargo.lock
index c4314b41e3966..a0db98f95b0c3 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -536,6 +536,14 @@ name = "either"
 version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "ena"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "enum_primitive"
 version = "0.1.1"
@@ -1649,6 +1657,7 @@ dependencies = [
 name = "rustc_data_structures"
 version = "0.0.0"
 dependencies = [
+ "ena 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "serialize 0.0.0",
 ]
@@ -2633,6 +2642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum duct 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e45aa15fe0a8a8f511e6d834626afd55e49b62e5c8802e18328a87e8a8f6065c"
 "checksum either 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e311a7479512fbdf858fb54d91ec59f3b9f85bc0113659f46bba12b199d273ce"
+"checksum ena 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb80e4764284ff0ec7054cb05c557f5ba01ccf65ff0c265e981c0b303d0ffc"
 "checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180"
 "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
 "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index b1e3035c1abb9..5a230e5d87bf9 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -132,7 +132,7 @@ impl<'infcx, 'gcx, 'tcx> InferCtxt<'infcx, 'gcx, 'tcx> {
     {
         self.int_unification_table
             .borrow_mut()
-            .unify_var_value(vid, val)
+            .unify_var_value(vid, Some(val))
             .map_err(|e| int_unification_error(vid_is_expected, e))?;
         match val {
             IntType(v) => Ok(self.tcx.mk_mach_int(v)),
@@ -148,7 +148,7 @@ impl<'infcx, 'gcx, 'tcx> InferCtxt<'infcx, 'gcx, 'tcx> {
     {
         self.float_unification_table
             .borrow_mut()
-            .unify_var_value(vid, val)
+            .unify_var_value(vid, Some(ty::FloatVarValue(val)))
             .map_err(|e| float_unification_error(vid_is_expected, e))?;
         Ok(self.tcx.mk_mach_float(val))
     }
@@ -496,9 +496,9 @@ fn int_unification_error<'tcx>(a_is_expected: bool, v: (ty::IntVarValue, ty::Int
 }
 
 fn float_unification_error<'tcx>(a_is_expected: bool,
-                                 v: (ast::FloatTy, ast::FloatTy))
+                                 v: (ty::FloatVarValue, ty::FloatVarValue))
                                  -> TypeError<'tcx>
 {
-    let (a, b) = v;
+    let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
     TypeError::FloatMismatch(ty::relate::expected_found_bool(a_is_expected, &a, &b))
 }
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index 41e7dffe54dc1..31f4bff6a0c0e 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -222,7 +222,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
             ty::TyInfer(ty::IntVar(v)) => {
                 self.freshen(
                     self.infcx.int_unification_table.borrow_mut()
-                                                    .probe(v)
+                                                    .probe_value(v)
                                                     .map(|v| v.to_type(tcx)),
                     ty::IntVar(v),
                     ty::FreshIntTy)
@@ -231,7 +231,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
             ty::TyInfer(ty::FloatVar(v)) => {
                 self.freshen(
                     self.infcx.float_unification_table.borrow_mut()
-                                                      .probe(v)
+                                                      .probe_value(v)
                                                       .map(|v| v.to_type(tcx)),
                     ty::FloatVar(v),
                     ty::FreshFloatTy)
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 77a617dd78de4..9d9fa3480d0a2 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -30,7 +30,7 @@ use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use ty::relate::RelateResult;
 use traits::{self, ObligationCause, PredicateObligations, Reveal};
-use rustc_data_structures::unify::{self, UnificationTable};
+use rustc_data_structures::unify as ut;
 use std::cell::{Cell, RefCell, Ref};
 use std::fmt;
 use syntax::ast;
@@ -93,10 +93,10 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     pub type_variables: RefCell<type_variable::TypeVariableTable<'tcx>>,
 
     // Map from integral variable to the kind of integer it represents
-    int_unification_table: RefCell<UnificationTable<ty::IntVid>>,
+    int_unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::IntVid>>>,
 
     // Map from floating variable to the kind of float it represents
-    float_unification_table: RefCell<UnificationTable<ty::FloatVid>>,
+    float_unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::FloatVid>>>,
 
     // For region variables.
     region_vars: RegionVarBindings<'a, 'gcx, 'tcx>,
@@ -377,8 +377,8 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
             in_progress_tables,
             projection_cache: RefCell::new(traits::ProjectionCache::new()),
             type_variables: RefCell::new(type_variable::TypeVariableTable::new()),
-            int_unification_table: RefCell::new(UnificationTable::new()),
-            float_unification_table: RefCell::new(UnificationTable::new()),
+            int_unification_table: RefCell::new(ut::UnificationTable::new()),
+            float_unification_table: RefCell::new(ut::UnificationTable::new()),
             region_vars: RegionVarBindings::new(tcx),
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
@@ -410,8 +410,8 @@ impl<'tcx, T> InferOk<'tcx, T> {
 pub struct CombinedSnapshot<'a, 'tcx:'a> {
     projection_cache_snapshot: traits::ProjectionCacheSnapshot,
     type_snapshot: type_variable::Snapshot,
-    int_snapshot: unify::Snapshot<ty::IntVid>,
-    float_snapshot: unify::Snapshot<ty::FloatVid>,
+    int_snapshot: ut::Snapshot<ut::InPlace<ty::IntVid>>,
+    float_snapshot: ut::Snapshot<ut::InPlace<ty::FloatVid>>,
     region_vars_snapshot: RegionSnapshot,
     was_in_snapshot: bool,
     _in_progress_tables: Option<Ref<'a, ty::TypeckTables<'tcx>>>,
@@ -611,14 +611,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         use ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
         match ty.sty {
             ty::TyInfer(ty::IntVar(vid)) => {
-                if self.int_unification_table.borrow_mut().has_value(vid) {
+                if self.int_unification_table.borrow_mut().probe_value(vid).is_some() {
                     Neither
                 } else {
                     UnconstrainedInt
                 }
             },
             ty::TyInfer(ty::FloatVar(vid)) => {
-                if self.float_unification_table.borrow_mut().has_value(vid) {
+                if self.float_unification_table.borrow_mut().probe_value(vid).is_some() {
                     Neither
                 } else {
                     UnconstrainedFloat
@@ -631,27 +631,32 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> {
         let mut variables = Vec::new();
 
-        let unbound_ty_vars = self.type_variables
-                                  .borrow_mut()
-                                  .unsolved_variables()
-                                  .into_iter()
-                                  .map(|t| self.tcx.mk_var(t));
-
-        let unbound_int_vars = self.int_unification_table
-                                   .borrow_mut()
-                                   .unsolved_variables()
-                                   .into_iter()
-                                   .map(|v| self.tcx.mk_int_var(v));
+        {
+            let mut type_variables = self.type_variables.borrow_mut();
+            variables.extend(
+                type_variables
+                    .unsolved_variables()
+                    .into_iter()
+                    .map(|t| self.tcx.mk_var(t)));
+        }
 
-        let unbound_float_vars = self.float_unification_table
-                                     .borrow_mut()
-                                     .unsolved_variables()
-                                     .into_iter()
-                                     .map(|v| self.tcx.mk_float_var(v));
+        {
+            let mut int_unification_table = self.int_unification_table.borrow_mut();
+            variables.extend(
+                (0..int_unification_table.len())
+                    .map(|i| ty::IntVid { index: i as u32 })
+                    .filter(|&vid| int_unification_table.probe_value(vid).is_none())
+                    .map(|v| self.tcx.mk_int_var(v)));
+        }
 
-        variables.extend(unbound_ty_vars);
-        variables.extend(unbound_int_vars);
-        variables.extend(unbound_float_vars);
+        {
+            let mut float_unification_table = self.float_unification_table.borrow_mut();
+            variables.extend(
+                (0..float_unification_table.len())
+                    .map(|i| ty::FloatVid { index: i as u32 })
+                    .filter(|&vid| float_unification_table.probe_value(vid).is_none())
+                    .map(|v| self.tcx.mk_float_var(v)));
+        }
 
         return variables;
     }
@@ -1093,7 +1098,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             ty::TyInfer(ty::IntVar(v)) => {
                 self.int_unification_table
                     .borrow_mut()
-                    .probe(v)
+                    .probe_value(v)
                     .map(|v| v.to_type(self.tcx))
                     .unwrap_or(typ)
             }
@@ -1101,7 +1106,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             ty::TyInfer(ty::FloatVar(v)) => {
                 self.float_unification_table
                     .borrow_mut()
-                    .probe(v)
+                    .probe_value(v)
                     .map(|v| v.to_type(self.tcx))
                     .unwrap_or(typ)
             }
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 8351be490767a..5db162848bfe9 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -21,7 +21,7 @@ use super::unify_key;
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING};
-use rustc_data_structures::unify::{self, UnificationTable};
+use rustc_data_structures::unify as ut;
 use middle::free_region::RegionRelations;
 use ty::{self, Ty, TyCtxt};
 use ty::{Region, RegionVid};
@@ -230,7 +230,7 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     /// back.
     undo_log: RefCell<Vec<UndoLogEntry<'tcx>>>,
 
-    unification_table: RefCell<UnificationTable<ty::RegionVid>>,
+    unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::RegionVid>>>,
 
     /// This contains the results of inference.  It begins as an empty
     /// option and only acquires a value after inference is complete.
@@ -239,7 +239,7 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
 pub struct RegionSnapshot {
     length: usize,
-    region_snapshot: unify::Snapshot<ty::RegionVid>,
+    region_snapshot: ut::Snapshot<ut::InPlace<ty::RegionVid>>,
     skolemization_count: u32,
 }
 
@@ -365,7 +365,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             skolemization_count: Cell::new(0),
             bound_count: Cell::new(0),
             undo_log: RefCell::new(Vec::new()),
-            unification_table: RefCell::new(UnificationTable::new()),
+            unification_table: RefCell::new(ut::UnificationTable::new()),
         }
     }
 
@@ -808,7 +808,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
     }
 
     pub fn opportunistic_resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> {
-        let vid = self.unification_table.borrow_mut().find_value(rid).min_vid;
+        let vid = self.unification_table.borrow_mut().probe_value(rid).min_vid;
         self.tcx.mk_region(ty::ReVar(vid))
     }
 
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index 209c19601f5e4..72600b8153deb 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -26,7 +26,7 @@ pub struct TypeVariableTable<'tcx> {
 
     /// Two variables are unified in `eq_relations` when we have a
     /// constraint `?X == ?Y`.
-    eq_relations: ut::UnificationTable<ty::TyVid>,
+    eq_relations: ut::UnificationTable<ut::InPlace<ty::TyVid>>,
 
     /// Two variables are unified in `eq_relations` when we have a
     /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second
@@ -45,7 +45,7 @@ pub struct TypeVariableTable<'tcx> {
     /// This is reasonable because, in Rust, subtypes have the same
     /// "skeleton" and hence there is no possible type such that
     /// (e.g.)  `Box<?3> <: ?3` for any `?3`.
-    sub_relations: ut::UnificationTable<ty::TyVid>,
+    sub_relations: ut::UnificationTable<ut::InPlace<ty::TyVid>>,
 }
 
 /// Reasons to create a type inference variable
@@ -83,8 +83,8 @@ enum TypeVariableValue<'tcx> {
 
 pub struct Snapshot {
     snapshot: sv::Snapshot,
-    eq_snapshot: ut::Snapshot<ty::TyVid>,
-    sub_snapshot: ut::Snapshot<ty::TyVid>,
+    eq_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
+    sub_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
 }
 
 struct Instantiate {
@@ -351,3 +351,10 @@ impl<'tcx> sv::SnapshotVecDelegate for Delegate<'tcx> {
         values[vid.index as usize].value = Unknown;
     }
 }
+
+impl ut::UnifyKey for ty::TyVid {
+    type Value = ();
+    fn index(&self) -> u32 { self.index }
+    fn from_index(i: u32) -> ty::TyVid { ty::TyVid { index: i } }
+    fn tag() -> &'static str { "TyVid" }
+}
diff --git a/src/librustc/infer/unify_key.rs b/src/librustc/infer/unify_key.rs
index d7e3a53ff25c9..24e3aa51e290a 100644
--- a/src/librustc/infer/unify_key.rs
+++ b/src/librustc/infer/unify_key.rs
@@ -8,9 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syntax::ast;
-use ty::{self, IntVarValue, Ty, TyCtxt};
-use rustc_data_structures::unify::{Combine, UnifyKey};
+use ty::{self, FloatVarValue, IntVarValue, Ty, TyCtxt};
+use rustc_data_structures::unify::{NoError, EqUnifyValue, UnifyKey, UnifyValue};
 
 pub trait ToType {
     fn to_type<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>;
@@ -20,7 +19,10 @@ impl UnifyKey for ty::IntVid {
     type Value = Option<IntVarValue>;
     fn index(&self) -> u32 { self.index }
     fn from_index(i: u32) -> ty::IntVid { ty::IntVid { index: i } }
-    fn tag(_: Option<ty::IntVid>) -> &'static str { "IntVid" }
+    fn tag() -> &'static str { "IntVid" }
+}
+
+impl EqUnifyValue for IntVarValue {
 }
 
 #[derive(PartialEq, Copy, Clone, Debug)]
@@ -31,15 +33,17 @@ pub struct RegionVidKey {
     pub min_vid: ty::RegionVid
 }
 
-impl Combine for RegionVidKey {
-    fn combine(&self, other: &RegionVidKey) -> RegionVidKey {
-        let min_vid = if self.min_vid.index < other.min_vid.index {
-            self.min_vid
+impl UnifyValue for RegionVidKey {
+    type Error = NoError;
+
+    fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
+        let min_vid = if value1.min_vid.index < value2.min_vid.index {
+            value1.min_vid
         } else {
-            other.min_vid
+            value2.min_vid
         };
 
-        RegionVidKey { min_vid: min_vid }
+        Ok(RegionVidKey { min_vid: min_vid })
     }
 }
 
@@ -47,7 +51,7 @@ impl UnifyKey for ty::RegionVid {
     type Value = RegionVidKey;
     fn index(&self) -> u32 { self.index }
     fn from_index(i: u32) -> ty::RegionVid { ty::RegionVid { index: i } }
-    fn tag(_: Option<ty::RegionVid>) -> &'static str { "RegionVid" }
+    fn tag() -> &'static str { "RegionVid" }
 }
 
 impl ToType for IntVarValue {
@@ -62,21 +66,17 @@ impl ToType for IntVarValue {
 // Floating point type keys
 
 impl UnifyKey for ty::FloatVid {
-    type Value = Option<ast::FloatTy>;
+    type Value = Option<FloatVarValue>;
     fn index(&self) -> u32 { self.index }
     fn from_index(i: u32) -> ty::FloatVid { ty::FloatVid { index: i } }
-    fn tag(_: Option<ty::FloatVid>) -> &'static str { "FloatVid" }
+    fn tag() -> &'static str { "FloatVid" }
 }
 
-impl ToType for ast::FloatTy {
-    fn to_type<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
-        tcx.mk_mach_float(*self)
-    }
+impl EqUnifyValue for FloatVarValue {
 }
 
-impl UnifyKey for ty::TyVid {
-    type Value = ();
-    fn index(&self) -> u32 { self.index }
-    fn from_index(i: u32) -> ty::TyVid { ty::TyVid { index: i } }
-    fn tag(_: Option<ty::TyVid>) -> &'static str { "TyVid" }
+impl ToType for FloatVarValue {
+    fn to_type<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
+        tcx.mk_mach_float(self.0)
+    }
 }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index bfd7681644a4a..8fbc14023ce66 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -655,12 +655,15 @@ pub struct ClosureUpvar<'tcx> {
     pub ty: Ty<'tcx>,
 }
 
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, Copy, PartialEq, Eq)]
 pub enum IntVarValue {
     IntType(ast::IntTy),
     UintType(ast::UintTy),
 }
 
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct FloatVarValue(pub ast::FloatTy);
+
 #[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
 pub struct TypeParameterDef {
     pub name: Name,
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index acb929981fbf2..73dfc01718eb1 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -888,6 +888,12 @@ impl fmt::Debug for ty::IntVarValue {
     }
 }
 
+impl fmt::Debug for ty::FloatVarValue {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
 // The generic impl doesn't work yet because projections are not
 // normalized under HRTB.
 /*impl<T> fmt::Display for ty::Binder<T>
diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml
index 343b1ed68b804..381b89a238fd6 100644
--- a/src/librustc_data_structures/Cargo.toml
+++ b/src/librustc_data_structures/Cargo.toml
@@ -9,5 +9,6 @@ path = "lib.rs"
 crate-type = ["dylib"]
 
 [dependencies]
+ena = "0.8.0"
 log = "0.3"
 serialize = { path = "../libserialize" }
diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs
index 3a20343033c23..e32ab10de4824 100644
--- a/src/librustc_data_structures/lib.rs
+++ b/src/librustc_data_structures/lib.rs
@@ -36,6 +36,7 @@
 #![cfg_attr(test, feature(test))]
 
 extern crate core;
+extern crate ena;
 #[macro_use]
 extern crate log;
 extern crate serialize as rustc_serialize; // used by deriving
@@ -57,10 +58,10 @@ pub mod indexed_vec;
 pub mod obligation_forest;
 pub mod sip128;
 pub mod snapshot_map;
-pub mod snapshot_vec;
+pub use ena::snapshot_vec;
 pub mod stable_hasher;
 pub mod transitive_relation;
-pub mod unify;
+pub use ena::unify;
 pub mod fx;
 pub mod tuple_slice;
 pub mod veccell;

From 2b7f276f74fabbdd4f6b7b388350912a5b77f8ed Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 16 Jul 2017 08:32:21 -0400
Subject: [PATCH 09/63] store type values in the unification table directly

---
 src/librustc/infer/combine.rs           |   4 +-
 src/librustc/infer/higher_ranked/mod.rs |  17 +-
 src/librustc/infer/mod.rs               |  14 +-
 src/librustc/infer/type_variable.rs     | 223 ++++++++++++++++--------
 src/librustc/traits/select.rs           |  16 +-
 5 files changed, 176 insertions(+), 98 deletions(-)

diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 5a230e5d87bf9..44a214a3d3633 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -388,7 +388,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                     // `vid` are related via subtyping.
                     return Err(TypeError::CyclicTy);
                 } else {
-                    match variables.probe_root(vid) {
+                    match variables.probe(vid) {
                         Some(u) => {
                             drop(variables);
                             self.relate(&u, &u)
@@ -409,7 +409,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                                 ty::Covariant | ty::Contravariant => (),
                             }
 
-                            let origin = variables.origin(vid);
+                            let origin = *variables.var_origin(vid);
                             let new_var_id = variables.new_var(false, origin);
                             let u = self.tcx().mk_var(new_var_id);
                             debug!("generalize: replacing original vid={:?} with new={:?}",
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index 6736751a5a2c2..ba6dde3ba6f50 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -242,7 +242,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
 
         fn generalize_region<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                              span: Span,
-                                             snapshot: &CombinedSnapshot,
+                                             snapshot: &CombinedSnapshot<'a, 'tcx>,
                                              debruijn: ty::DebruijnIndex,
                                              new_vars: &[ty::RegionVid],
                                              a_map: &FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
@@ -338,7 +338,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
 
         fn generalize_region<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                              span: Span,
-                                             snapshot: &CombinedSnapshot,
+                                             snapshot: &CombinedSnapshot<'a, 'tcx>,
                                              debruijn: ty::DebruijnIndex,
                                              new_vars: &[ty::RegionVid],
                                              a_map: &FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
@@ -477,7 +477,7 @@ fn fold_regions_in<'a, 'gcx, 'tcx, T, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     fn tainted_regions(&self,
-                       snapshot: &CombinedSnapshot,
+                       snapshot: &CombinedSnapshot<'a, 'tcx>,
                        r: ty::Region<'tcx>,
                        directions: TaintDirections)
                        -> FxHashSet<ty::Region<'tcx>> {
@@ -485,7 +485,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 
     fn region_vars_confined_to_snapshot(&self,
-                                        snapshot: &CombinedSnapshot)
+                                        snapshot: &CombinedSnapshot<'a, 'tcx>)
                                         -> Vec<ty::RegionVid>
     {
         /*!
@@ -576,7 +576,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// See `README.md` for more details.
     pub fn skolemize_late_bound_regions<T>(&self,
                                            binder: &ty::Binder<T>,
-                                           snapshot: &CombinedSnapshot)
+                                           snapshot: &CombinedSnapshot<'a, 'tcx>)
                                            -> (T, SkolemizationMap<'tcx>)
         where T : TypeFoldable<'tcx>
     {
@@ -601,7 +601,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                       overly_polymorphic: bool,
                       _span: Span,
                       skol_map: &SkolemizationMap<'tcx>,
-                      snapshot: &CombinedSnapshot)
+                      snapshot: &CombinedSnapshot<'a, 'tcx>)
                       -> RelateResult<'tcx, ()>
     {
         debug!("leak_check: skol_map={:?}",
@@ -676,7 +676,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// predicate is `for<'a> &'a int : Clone`.
     pub fn plug_leaks<T>(&self,
                          skol_map: SkolemizationMap<'tcx>,
-                         snapshot: &CombinedSnapshot,
+                         snapshot: &CombinedSnapshot<'a, 'tcx>,
                          value: T) -> T
         where T : TypeFoldable<'tcx>
     {
@@ -762,8 +762,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// Note: popping also occurs implicitly as part of `leak_check`.
     pub fn pop_skolemized(&self,
                           skol_map: SkolemizationMap<'tcx>,
-                          snapshot: &CombinedSnapshot)
-    {
+                          snapshot: &CombinedSnapshot<'a, 'tcx>) {
         debug!("pop_skolemized({:?})", skol_map);
         let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect();
         self.region_vars.pop_skolemized(&skol_regions, &snapshot.region_vars_snapshot);
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 9d9fa3480d0a2..50f1a94b4c9eb 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -409,7 +409,7 @@ impl<'tcx, T> InferOk<'tcx, T> {
 #[must_use = "once you start a snapshot, you should always consume it"]
 pub struct CombinedSnapshot<'a, 'tcx:'a> {
     projection_cache_snapshot: traits::ProjectionCacheSnapshot,
-    type_snapshot: type_variable::Snapshot,
+    type_snapshot: type_variable::Snapshot<'tcx>,
     int_snapshot: ut::Snapshot<ut::InPlace<ty::IntVid>>,
     float_snapshot: ut::Snapshot<ut::InPlace<ty::FloatVid>>,
     region_vars_snapshot: RegionSnapshot,
@@ -698,7 +698,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         result
     }
 
-    fn start_snapshot<'b>(&'b self) -> CombinedSnapshot<'b, 'tcx> {
+    fn start_snapshot(&self) -> CombinedSnapshot<'a, 'tcx> {
         debug!("start_snapshot()");
 
         let in_snapshot = self.in_snapshot.get();
@@ -719,7 +719,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot) {
+    fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) {
         debug!("rollback_to(cause={})", cause);
         let CombinedSnapshot { projection_cache_snapshot,
                                type_snapshot,
@@ -747,7 +747,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             .rollback_to(region_vars_snapshot);
     }
 
-    fn commit_from(&self, snapshot: CombinedSnapshot) {
+    fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) {
         debug!("commit_from()");
         let CombinedSnapshot { projection_cache_snapshot,
                                type_snapshot,
@@ -788,7 +788,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
     /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`
     pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where
-        F: FnOnce(&CombinedSnapshot) -> Result<T, E>
+        F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result<T, E>
     {
         debug!("commit_if_ok()");
         let snapshot = self.start_snapshot();
@@ -803,7 +803,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
     // Execute `f` in a snapshot, and commit the bindings it creates
     pub fn in_snapshot<T, F>(&self, f: F) -> T where
-        F: FnOnce(&CombinedSnapshot) -> T
+        F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> T
     {
         debug!("in_snapshot()");
         let snapshot = self.start_snapshot();
@@ -814,7 +814,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
     /// Execute `f` then unroll any bindings it creates
     pub fn probe<R, F>(&self, f: F) -> R where
-        F: FnOnce(&CombinedSnapshot) -> R,
+        F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
     {
         debug!("probe()");
         let snapshot = self.start_snapshot();
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index 72600b8153deb..fcf7cc294d920 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -8,25 +8,24 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use self::TypeVariableValue::*;
 use syntax::ast;
 use syntax_pos::Span;
 use ty::{self, Ty};
 
 use std::cmp::min;
 use std::marker::PhantomData;
-use std::mem;
 use std::u32;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::snapshot_vec as sv;
 use rustc_data_structures::unify as ut;
 
 pub struct TypeVariableTable<'tcx> {
-    values: sv::SnapshotVec<Delegate<'tcx>>,
+    values: sv::SnapshotVec<Delegate>,
 
     /// Two variables are unified in `eq_relations` when we have a
-    /// constraint `?X == ?Y`.
-    eq_relations: ut::UnificationTable<ut::InPlace<ty::TyVid>>,
+    /// constraint `?X == ?Y`. This table also stores, for each key,
+    /// the known value.
+    eq_relations: ut::UnificationTable<ut::InPlace<TyVidEqKey<'tcx>>>,
 
     /// Two variables are unified in `eq_relations` when we have a
     /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second
@@ -68,22 +67,20 @@ pub enum TypeVariableOrigin {
 
 pub type TypeVariableMap = FxHashMap<ty::TyVid, TypeVariableOrigin>;
 
-struct TypeVariableData<'tcx> {
-    value: TypeVariableValue<'tcx>,
+struct TypeVariableData {
     origin: TypeVariableOrigin,
     diverging: bool
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 enum TypeVariableValue<'tcx> {
-    Known {
-        value: Ty<'tcx>
-    },
+    Known { value: Ty<'tcx> },
     Unknown,
 }
 
-pub struct Snapshot {
+pub struct Snapshot<'tcx> {
     snapshot: sv::Snapshot,
-    eq_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
+    eq_snapshot: ut::Snapshot<ut::InPlace<TyVidEqKey<'tcx>>>,
     sub_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
 }
 
@@ -91,7 +88,7 @@ struct Instantiate {
     vid: ty::TyVid,
 }
 
-struct Delegate<'tcx>(PhantomData<&'tcx ()>);
+struct Delegate;
 
 impl<'tcx> TypeVariableTable<'tcx> {
     pub fn new() -> TypeVariableTable<'tcx> {
@@ -102,10 +99,18 @@ impl<'tcx> TypeVariableTable<'tcx> {
         }
     }
 
+    /// Returns the diverges flag given when `vid` was created.
+    ///
+    /// Note that this function does not return care whether
+    /// `vid` has been unified with something else or not.
     pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool {
         self.values.get(vid.index as usize).diverging
     }
 
+    /// Returns the origin that was given when `vid` was created.
+    ///
+    /// Note that this function does not return care whether
+    /// `vid` has been unified with something else or not.
     pub fn var_origin(&self, vid: ty::TyVid) -> &TypeVariableOrigin {
         &self.values.get(vid.index as usize).origin
     }
@@ -134,41 +139,49 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// Precondition: `vid` must not have been previously instantiated.
     pub fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) {
         let vid = self.root_var(vid);
-        debug_assert!(self.probe_root(vid).is_none());
-
-        let old_value = {
-            let vid_data = &mut self.values[vid.index as usize];
-            mem::replace(&mut vid_data.value, TypeVariableValue::Known { value: ty })
-        };
-
-        match old_value {
-            TypeVariableValue::Unknown => {
-                self.values.record(Instantiate { vid: vid });
-            }
-            TypeVariableValue::Known { value: old_ty } => {
-                bug!("instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
-                     vid, ty, old_ty)
-            }
-        }
+        debug_assert!(self.probe(vid).is_none());
+        debug_assert!(self.eq_relations.probe_value(vid) == TypeVariableValue::Unknown,
+                      "instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
+                      vid, ty, self.eq_relations.probe_value(vid));
+        self.eq_relations.union_value(vid, TypeVariableValue::Known { value: ty });
+
+        // Hack: we only need this so that `types_escaping_snapshot`
+        // can see what has been unified; see the Delegate impl for
+        // more details.
+        self.values.record(Instantiate { vid: vid });
     }
 
+    /// Creates a new type variable.
+    ///
+    /// - `diverging`: indicates if this is a "diverging" type
+    ///   variable, e.g.  one created as the type of a `return`
+    ///   expression. The code in this module doesn't care if a
+    ///   variable is diverging, but the main Rust type-checker will
+    ///   sometimes "unify" such variables with the `!` or `()` types.
+    /// - `origin`: indicates *why* the type variable was created.
+    ///   The code in this module doesn't care, but it can be useful
+    ///   for improving error messages.
     pub fn new_var(&mut self,
                    diverging: bool,
                    origin: TypeVariableOrigin)
                    -> ty::TyVid {
-        debug!("new_var(diverging={:?}, origin={:?})", diverging, origin);
-        self.eq_relations.new_key(());
-        self.sub_relations.new_key(());
+        let eq_key = self.eq_relations.new_key(TypeVariableValue::Unknown);
+
+        let sub_key = self.sub_relations.new_key(());
+        assert_eq!(eq_key.vid, sub_key);
+
         let index = self.values.push(TypeVariableData {
-            value: Unknown,
             origin,
             diverging,
         });
-        let v = ty::TyVid { index: index as u32 };
-        debug!("new_var: diverging={:?} index={:?}", diverging, v);
-        v
+        assert_eq!(eq_key.vid.index, index as u32);
+
+        debug!("new_var(index={:?}, diverging={:?}, origin={:?}", eq_key.vid, diverging, origin);
+
+        eq_key.vid
     }
 
+    /// Returns the number of type variables created thus far.
     pub fn num_vars(&self) -> usize {
         self.values.len()
     }
@@ -179,7 +192,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// algorithm), so `root_var(a) == root_var(b)` implies that `a ==
     /// b` (transitively).
     pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
-        self.eq_relations.find(vid)
+        self.eq_relations.find(vid).vid
     }
 
     /// Returns the "root" variable of `vid` in the `sub_relations`
@@ -199,24 +212,19 @@ impl<'tcx> TypeVariableTable<'tcx> {
         self.sub_root_var(a) == self.sub_root_var(b)
     }
 
+    /// Retrieves the type to which `vid` has been instantiated, if
+    /// any.
     pub fn probe(&mut self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
         let vid = self.root_var(vid);
-        self.probe_root(vid)
-    }
-
-    pub fn origin(&self, vid: ty::TyVid) -> TypeVariableOrigin {
-        self.values.get(vid.index as usize).origin.clone()
-    }
-
-    /// Retrieves the type of `vid` given that it is currently a root in the unification table
-    pub fn probe_root(&mut self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
-        debug_assert!(self.root_var(vid) == vid);
-        match self.values.get(vid.index as usize).value {
-            Unknown => None,
-            Known { value } => Some(value)
+        match self.eq_relations.probe_value(vid) {
+            TypeVariableValue::Unknown => None,
+            TypeVariableValue::Known { value } => Some(value)
         }
     }
 
+    /// If `t` is a type-inference variable, and it has been
+    /// instantiated, then return the with which it was
+    /// instantiated. Otherwise, returns `t`.
     pub fn replace_if_possible(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
         match t.sty {
             ty::TyInfer(ty::TyVar(v)) => {
@@ -229,7 +237,11 @@ impl<'tcx> TypeVariableTable<'tcx> {
         }
     }
 
-    pub fn snapshot(&mut self) -> Snapshot {
+    /// Creates a snapshot of the type variable state.  This snapshot
+    /// must later be committed (`commit()`) or rolled back
+    /// (`rollback_to()`).  Nested snapshots are permitted, but must
+    /// be processed in a stack-like fashion.
+    pub fn snapshot(&mut self) -> Snapshot<'tcx> {
         Snapshot {
             snapshot: self.values.start_snapshot(),
             eq_snapshot: self.eq_relations.snapshot(),
@@ -237,7 +249,10 @@ impl<'tcx> TypeVariableTable<'tcx> {
         }
     }
 
-    pub fn rollback_to(&mut self, s: Snapshot) {
+    /// Undoes all changes since the snapshot was created. Any
+    /// snapshots created since that point must already have been
+    /// committed or rolled back.
+    pub fn rollback_to(&mut self, s: Snapshot<'tcx>) {
         debug!("rollback_to{:?}", {
             for action in self.values.actions_since_snapshot(&s.snapshot) {
                 match *action {
@@ -255,7 +270,11 @@ impl<'tcx> TypeVariableTable<'tcx> {
         self.sub_relations.rollback_to(sub_snapshot);
     }
 
-    pub fn commit(&mut self, s: Snapshot) {
+    /// Commits all changes since the snapshot was created, making
+    /// them permanent (unless this snapshot was created within
+    /// another snapshot). Any snapshots created since that point
+    /// must already have been committed or rolled back.
+    pub fn commit(&mut self, s: Snapshot<'tcx>) {
         let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
         self.values.commit(snapshot);
         self.eq_relations.commit(eq_snapshot);
@@ -266,7 +285,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// ty-variables created during the snapshot, and the values
     /// `{V2}` are the root variables that they were unified with,
     /// along with their origin.
-    pub fn types_created_since_snapshot(&mut self, s: &Snapshot) -> TypeVariableMap {
+    pub fn types_created_since_snapshot(&mut self, s: &Snapshot<'tcx>) -> TypeVariableMap {
         let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
 
         actions_since_snapshot
@@ -282,16 +301,13 @@ impl<'tcx> TypeVariableTable<'tcx> {
             .collect()
     }
 
-    pub fn types_escaping_snapshot(&mut self, s: &Snapshot) -> Vec<Ty<'tcx>> {
-        /*!
-         * Find the set of type variables that existed *before* `s`
-         * but which have only been unified since `s` started, and
-         * return the types with which they were unified. So if we had
-         * a type variable `V0`, then we started the snapshot, then we
-         * created a type variable `V1`, unifed `V0` with `T0`, and
-         * unified `V1` with `T1`, this function would return `{T0}`.
-         */
-
+    /// Find the set of type variables that existed *before* `s`
+    /// but which have only been unified since `s` started, and
+    /// return the types with which they were unified. So if we had
+    /// a type variable `V0`, then we started the snapshot, then we
+    /// created a type variable `V1`, unifed `V0` with `T0`, and
+    /// unified `V1` with `T1`, this function would return `{T0}`.
+    pub fn types_escaping_snapshot(&mut self, s: &Snapshot<'tcx>) -> Vec<Ty<'tcx>> {
         let mut new_elem_threshold = u32::MAX;
         let mut escaping_types = Vec::new();
         let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
@@ -312,9 +328,9 @@ impl<'tcx> TypeVariableTable<'tcx> {
                     if vid.index < new_elem_threshold {
                         // quick check to see if this variable was
                         // created since the snapshot started or not.
-                        let escaping_type = match self.values.get(vid.index as usize).value {
-                            Unknown => bug!(),
-                            Known { value } => value,
+                        let escaping_type = match self.eq_relations.probe_value(vid) {
+                            TypeVariableValue::Unknown => bug!(),
+                            TypeVariableValue::Known { value } => value,
                         };
                         escaping_types.push(escaping_type);
                     }
@@ -328,6 +344,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
         escaping_types
     }
 
+    /// Returns indices of all variables that are not yet
+    /// instantiated.
     pub fn unsolved_variables(&mut self) -> Vec<ty::TyVid> {
         (0..self.values.len())
             .filter_map(|i| {
@@ -342,19 +360,80 @@ impl<'tcx> TypeVariableTable<'tcx> {
     }
 }
 
-impl<'tcx> sv::SnapshotVecDelegate for Delegate<'tcx> {
-    type Value = TypeVariableData<'tcx>;
+impl sv::SnapshotVecDelegate for Delegate {
+    type Value = TypeVariableData;
     type Undo = Instantiate;
 
-    fn reverse(values: &mut Vec<TypeVariableData<'tcx>>, action: Instantiate) {
-        let Instantiate { vid } = action;
-        values[vid.index as usize].value = Unknown;
+    fn reverse(_values: &mut Vec<TypeVariableData>, _action: Instantiate) {
+        // We don't actually have to *do* anything to reverse an
+        // instanation; the value for a variable is stored in the
+        // `eq_relations` and hence its rollback code will handle
+        // it. In fact, we could *almost* just remove the
+        // `SnapshotVec` entirely, except that we would have to
+        // reproduce *some* of its logic, since we want to know which
+        // type variables have been instantiated since the snapshot
+        // was started, so we can implement `types_escaping_snapshot`.
+        //
+        // (If we extended the `UnificationTable` to let us see which
+        // values have been unified and so forth, that might also
+        // suffice.)
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/// These structs (a newtyped TyVid) are used as the unification key
+/// for the `eq_relations`; they carry a `TypeVariableValue` along
+/// with them.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+struct TyVidEqKey<'tcx> {
+    vid: ty::TyVid,
+
+    // in the table, we map each ty-vid to one of these:
+    phantom: PhantomData<TypeVariableValue<'tcx>>,
+}
+
+impl<'tcx> From<ty::TyVid> for TyVidEqKey<'tcx> {
+    fn from(vid: ty::TyVid) -> Self {
+        TyVidEqKey { vid, phantom: PhantomData }
+    }
+}
+
+impl<'tcx> ut::UnifyKey for TyVidEqKey<'tcx> {
+    type Value = TypeVariableValue<'tcx>;
+    fn index(&self) -> u32 { self.vid.index }
+    fn from_index(i: u32) -> Self { TyVidEqKey::from(ty::TyVid { index: i }) }
+    fn tag() -> &'static str { "TyVidEqKey" }
+}
+
+impl<'tcx> ut::UnifyValue for TypeVariableValue<'tcx> {
+    type Error = ut::NoError;
+
+    fn unify_values(value1: &Self, value2: &Self) -> Result<Self, ut::NoError> {
+        match (value1, value2) {
+            // We never equate two type variables, both of which
+            // have known types.  Instead, we recursively equate
+            // those types.
+            (&TypeVariableValue::Known { .. }, &TypeVariableValue::Known { .. }) => {
+                bug!("equating two type variables, both of which have known types")
+            }
+
+            // If one side is known, prefer that one.
+            (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => Ok(*value1),
+            (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => Ok(*value2),
+
+            // If both sides are *unknown*, it hardly matters, does it?
+            (&TypeVariableValue::Unknown, &TypeVariableValue::Unknown) => Ok(*value1),
+        }
     }
 }
 
+/// Raw `TyVid` are used as the unification key for `sub_relations`;
+/// they carry no values.
 impl ut::UnifyKey for ty::TyVid {
     type Value = ();
     fn index(&self) -> u32 { self.index }
     fn from_index(i: u32) -> ty::TyVid { ty::TyVid { index: i } }
     fn tag() -> &'static str { "TyVid" }
 }
+
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 7716770d318ba..514f251452c78 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -452,7 +452,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// 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
-        where F: FnOnce(&mut Self, &infer::CombinedSnapshot) -> R
+        where F: FnOnce(&mut Self, &infer::CombinedSnapshot<'cx, 'tcx>) -> R
     {
         // The irrefutable nature of the operation means we don't need to snapshot the
         // inferred_obligations vector.
@@ -462,7 +462,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// Wraps a probe s.t. obligations collected during it are ignored and old obligations are
     /// retained.
     fn probe<R, F>(&mut self, f: F) -> R
-        where F: FnOnce(&mut Self, &infer::CombinedSnapshot) -> R
+        where F: FnOnce(&mut Self, &infer::CombinedSnapshot<'cx, 'tcx>) -> R
     {
         let inferred_obligations_snapshot = self.inferred_obligations.start_snapshot();
         let result = self.infcx.probe(|snapshot| f(self, snapshot));
@@ -1407,7 +1407,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn match_projection_obligation_against_definition_bounds(
         &mut self,
         obligation: &TraitObligation<'tcx>,
-        snapshot: &infer::CombinedSnapshot)
+        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
         -> bool
     {
         let poly_trait_predicate =
@@ -1478,7 +1478,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         trait_bound: ty::PolyTraitRef<'tcx>,
                         skol_trait_ref: ty::TraitRef<'tcx>,
                         skol_map: &infer::SkolemizationMap<'tcx>,
-                        snapshot: &infer::CombinedSnapshot)
+                        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
                         -> bool
     {
         assert!(!skol_trait_ref.has_escaping_regions());
@@ -2507,7 +2507,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                    recursion_depth: usize,
                    param_env: ty::ParamEnv<'tcx>,
                    skol_map: infer::SkolemizationMap<'tcx>,
-                   snapshot: &infer::CombinedSnapshot)
+                   snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
                    -> VtableImplData<'tcx, PredicateObligation<'tcx>>
     {
         debug!("vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={}, skol_map={:?})",
@@ -2996,7 +2996,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn rematch_impl(&mut self,
                     impl_def_id: DefId,
                     obligation: &TraitObligation<'tcx>,
-                    snapshot: &infer::CombinedSnapshot)
+                    snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
                     -> (Normalized<'tcx, &'tcx Substs<'tcx>>,
                         infer::SkolemizationMap<'tcx>)
     {
@@ -3013,7 +3013,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn match_impl(&mut self,
                   impl_def_id: DefId,
                   obligation: &TraitObligation<'tcx>,
-                  snapshot: &infer::CombinedSnapshot)
+                  snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
                   -> Result<(Normalized<'tcx, &'tcx Substs<'tcx>>,
                              infer::SkolemizationMap<'tcx>), ()>
     {
@@ -3210,7 +3210,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                  def_id: DefId, // of impl or trait
                                  substs: &Substs<'tcx>, // for impl or trait
                                  skol_map: infer::SkolemizationMap<'tcx>,
-                                 snapshot: &infer::CombinedSnapshot)
+                                 snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
                                  -> Vec<PredicateObligation<'tcx>>
     {
         debug!("impl_or_trait_obligations(def_id={:?})", def_id);

From 66b961b66db9fc968e499092c4d87adb4c08319c Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 16 Jul 2017 08:49:21 -0400
Subject: [PATCH 10/63] correct subtle bug in the type variable code

---
 src/librustc/infer/type_variable.rs | 171 ++++++++++------------------
 1 file changed, 63 insertions(+), 108 deletions(-)

diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index fcf7cc294d920..cda2fdac6f274 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -12,15 +12,18 @@ use syntax::ast;
 use syntax_pos::Span;
 use ty::{self, Ty};
 
-use std::cmp::min;
 use std::marker::PhantomData;
 use std::u32;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::snapshot_vec as sv;
 use rustc_data_structures::unify as ut;
 
 pub struct TypeVariableTable<'tcx> {
-    values: sv::SnapshotVec<Delegate>,
+    /// Extra data for each type variable, such as the origin. This is
+    /// not stored in the unification table since, when we inquire
+    /// after the origin of a variable X, we want the origin of **that
+    /// variable X**, not the origin of some other variable Y with
+    /// which X has been unified.
+    var_data: Vec<TypeVariableData>,
 
     /// Two variables are unified in `eq_relations` when we have a
     /// constraint `?X == ?Y`. This table also stores, for each key,
@@ -79,21 +82,20 @@ enum TypeVariableValue<'tcx> {
 }
 
 pub struct Snapshot<'tcx> {
-    snapshot: sv::Snapshot,
+    /// number of variables at the time of the snapshot
+    num_vars: usize,
+
+    /// snapshot from the `eq_relations` table
     eq_snapshot: ut::Snapshot<ut::InPlace<TyVidEqKey<'tcx>>>,
-    sub_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
-}
 
-struct Instantiate {
-    vid: ty::TyVid,
+    /// snapshot from the `sub_relations` table
+    sub_snapshot: ut::Snapshot<ut::InPlace<ty::TyVid>>,
 }
 
-struct Delegate;
-
 impl<'tcx> TypeVariableTable<'tcx> {
     pub fn new() -> TypeVariableTable<'tcx> {
         TypeVariableTable {
-            values: sv::SnapshotVec::new(),
+            var_data: Vec::new(),
             eq_relations: ut::UnificationTable::new(),
             sub_relations: ut::UnificationTable::new(),
         }
@@ -104,7 +106,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// Note that this function does not return care whether
     /// `vid` has been unified with something else or not.
     pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool {
-        self.values.get(vid.index as usize).diverging
+        self.var_data[vid.index as usize].diverging
     }
 
     /// Returns the origin that was given when `vid` was created.
@@ -112,7 +114,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// Note that this function does not return care whether
     /// `vid` has been unified with something else or not.
     pub fn var_origin(&self, vid: ty::TyVid) -> &TypeVariableOrigin {
-        &self.values.get(vid.index as usize).origin
+        &self.var_data[vid.index as usize].origin
     }
 
     /// Records that `a == b`, depending on `dir`.
@@ -144,11 +146,6 @@ impl<'tcx> TypeVariableTable<'tcx> {
                       "instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
                       vid, ty, self.eq_relations.probe_value(vid));
         self.eq_relations.union_value(vid, TypeVariableValue::Known { value: ty });
-
-        // Hack: we only need this so that `types_escaping_snapshot`
-        // can see what has been unified; see the Delegate impl for
-        // more details.
-        self.values.record(Instantiate { vid: vid });
     }
 
     /// Creates a new type variable.
@@ -170,11 +167,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
         let sub_key = self.sub_relations.new_key(());
         assert_eq!(eq_key.vid, sub_key);
 
-        let index = self.values.push(TypeVariableData {
-            origin,
-            diverging,
-        });
-        assert_eq!(eq_key.vid.index, index as u32);
+        assert_eq!(self.var_data.len(), sub_key.index as usize);
+        self.var_data.push(TypeVariableData { origin, diverging });
 
         debug!("new_var(index={:?}, diverging={:?}, origin={:?}", eq_key.vid, diverging, origin);
 
@@ -183,7 +177,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
 
     /// Returns the number of type variables created thus far.
     pub fn num_vars(&self) -> usize {
-        self.values.len()
+        self.var_data.len()
     }
 
     /// Returns the "root" variable of `vid` in the `eq_relations`
@@ -243,7 +237,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// be processed in a stack-like fashion.
     pub fn snapshot(&mut self) -> Snapshot<'tcx> {
         Snapshot {
-            snapshot: self.values.start_snapshot(),
+            num_vars: self.var_data.len(),
             eq_snapshot: self.eq_relations.snapshot(),
             sub_snapshot: self.sub_relations.snapshot(),
         }
@@ -253,21 +247,12 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// snapshots created since that point must already have been
     /// committed or rolled back.
     pub fn rollback_to(&mut self, s: Snapshot<'tcx>) {
-        debug!("rollback_to{:?}", {
-            for action in self.values.actions_since_snapshot(&s.snapshot) {
-                match *action {
-                    sv::UndoLog::NewElem(index) => {
-                        debug!("inference variable _#{}t popped", index)
-                    }
-                    _ => { }
-                }
-            }
-        });
-
-        let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
-        self.values.rollback_to(snapshot);
+        let Snapshot { num_vars, eq_snapshot, sub_snapshot } = s;
+        debug!("type_variables::rollback_to(num_vars = {})", num_vars);
+        assert!(self.var_data.len() >= num_vars);
         self.eq_relations.rollback_to(eq_snapshot);
         self.sub_relations.rollback_to(sub_snapshot);
+        self.var_data.truncate(num_vars);
     }
 
     /// Commits all changes since the snapshot was created, making
@@ -275,8 +260,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// another snapshot). Any snapshots created since that point
     /// must already have been committed or rolled back.
     pub fn commit(&mut self, s: Snapshot<'tcx>) {
-        let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s;
-        self.values.commit(snapshot);
+        let Snapshot { num_vars, eq_snapshot, sub_snapshot } = s;
+        debug!("type_variables::commit(num_vars = {})", num_vars);
         self.eq_relations.commit(eq_snapshot);
         self.sub_relations.commit(sub_snapshot);
     }
@@ -285,19 +270,12 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// ty-variables created during the snapshot, and the values
     /// `{V2}` are the root variables that they were unified with,
     /// along with their origin.
-    pub fn types_created_since_snapshot(&mut self, s: &Snapshot<'tcx>) -> TypeVariableMap {
-        let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
-
-        actions_since_snapshot
+    pub fn types_created_since_snapshot(&mut self, snapshot: &Snapshot<'tcx>) -> TypeVariableMap {
+        self.var_data
             .iter()
-            .filter_map(|action| match action {
-                &sv::UndoLog::NewElem(index) => Some(ty::TyVid { index: index as u32 }),
-                _ => None,
-            })
-            .map(|vid| {
-                let origin = self.values.get(vid.index as usize).origin.clone();
-                (vid, origin)
-            })
+            .enumerate()
+            .skip(snapshot.num_vars) // skip those that existed when snapshot was taken
+            .map(|(index, data)| (ty::TyVid { index: index as u32 }, data.origin))
             .collect()
     }
 
@@ -307,47 +285,45 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// a type variable `V0`, then we started the snapshot, then we
     /// created a type variable `V1`, unifed `V0` with `T0`, and
     /// unified `V1` with `T1`, this function would return `{T0}`.
-    pub fn types_escaping_snapshot(&mut self, s: &Snapshot<'tcx>) -> Vec<Ty<'tcx>> {
-        let mut new_elem_threshold = u32::MAX;
-        let mut escaping_types = Vec::new();
-        let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
-        debug!("actions_since_snapshot.len() = {}", actions_since_snapshot.len());
-        for action in actions_since_snapshot {
-            match *action {
-                sv::UndoLog::NewElem(index) => {
-                    // if any new variables were created during the
-                    // snapshot, remember the lower index (which will
-                    // always be the first one we see). Note that this
-                    // action must precede those variables being
-                    // specified.
-                    new_elem_threshold = min(new_elem_threshold, index as u32);
-                    debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold);
-                }
-
-                sv::UndoLog::Other(Instantiate { vid, .. }) => {
-                    if vid.index < new_elem_threshold {
-                        // quick check to see if this variable was
-                        // created since the snapshot started or not.
-                        let escaping_type = match self.eq_relations.probe_value(vid) {
-                            TypeVariableValue::Unknown => bug!(),
-                            TypeVariableValue::Known { value } => value,
-                        };
-                        escaping_types.push(escaping_type);
-                    }
-                    debug!("SpecifyVar({:?}) new_elem_threshold={}", vid, new_elem_threshold);
-                }
-
-                _ => { }
-            }
-        }
-
+    pub fn types_escaping_snapshot(&mut self, snapshot: &Snapshot<'tcx>) -> Vec<Ty<'tcx>> {
+        // We want to select only those instantiations that have
+        // occurred since the snapshot *and* which affect some
+        // variable that existed prior to the snapshot. This code just
+        // affects all instantiatons that ever occurred which affect
+        // variables prior to the snapshot.
+        //
+        // It's hard to do better than this, though, without changing
+        // the unification table to prefer "lower" vids -- the problem
+        // is that we may have a variable X (from before the snapshot)
+        // and Y (from after the snapshot) which get unified, with Y
+        // chosen as the new root. Now we are "instantiating" Y with a
+        // value, but it escapes into X, but we wouldn't readily see
+        // that. (In fact, earlier revisions of this code had this
+        // bug; it was introduced when we added the `eq_relations`
+        // table, but it's hard to create rust code that triggers it.)
+        //
+        // We could tell the table to prefer lower vids, and then we would
+        // see the case above, but we would get less-well-balanced trees.
+        //
+        // Since I hope to kill the leak-check in this branch, and
+        // that's the code which uses this logic anyway, I'm going to
+        // use the less efficient algorithm for now.
+        let mut escaping_types = Vec::with_capacity(snapshot.num_vars);
+        escaping_types.extend(
+            (0..snapshot.num_vars) // for all variables that pre-exist the snapshot...
+                .map(|i| ty::TyVid { index: i as u32 })
+                .filter_map(|vid| match self.eq_relations.probe_value(vid) {
+                    TypeVariableValue::Unknown => None,
+                    TypeVariableValue::Known { value } => Some(value),
+                })); // ...collect what types they've been instantiated with.
+        debug!("types_escaping_snapshot = {:?}", escaping_types);
         escaping_types
     }
 
     /// Returns indices of all variables that are not yet
     /// instantiated.
     pub fn unsolved_variables(&mut self) -> Vec<ty::TyVid> {
-        (0..self.values.len())
+        (0..self.var_data.len())
             .filter_map(|i| {
                 let vid = ty::TyVid { index: i as u32 };
                 if self.probe(vid).is_some() {
@@ -359,27 +335,6 @@ impl<'tcx> TypeVariableTable<'tcx> {
             .collect()
     }
 }
-
-impl sv::SnapshotVecDelegate for Delegate {
-    type Value = TypeVariableData;
-    type Undo = Instantiate;
-
-    fn reverse(_values: &mut Vec<TypeVariableData>, _action: Instantiate) {
-        // We don't actually have to *do* anything to reverse an
-        // instanation; the value for a variable is stored in the
-        // `eq_relations` and hence its rollback code will handle
-        // it. In fact, we could *almost* just remove the
-        // `SnapshotVec` entirely, except that we would have to
-        // reproduce *some* of its logic, since we want to know which
-        // type variables have been instantiated since the snapshot
-        // was started, so we can implement `types_escaping_snapshot`.
-        //
-        // (If we extended the `UnificationTable` to let us see which
-        // values have been unified and so forth, that might also
-        // suffice.)
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////
 
 /// These structs (a newtyped TyVid) are used as the unification key

From 7148ff7680af63e38808bef0f079bd5fa703aecb Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 16 Jul 2017 10:18:55 -0400
Subject: [PATCH 11/63] have `probe()` return `TypeVariableValue`

---
 src/librustc/infer/combine.rs       | 10 ++---
 src/librustc/infer/freshen.rs       |  2 +-
 src/librustc/infer/fudge.rs         |  4 +-
 src/librustc/infer/mod.rs           |  7 ++--
 src/librustc/infer/type_variable.rs | 58 ++++++++++++++++-------------
 5 files changed, 46 insertions(+), 35 deletions(-)

diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 44a214a3d3633..767e7e0e9509a 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -34,10 +34,10 @@
 
 use super::equate::Equate;
 use super::glb::Glb;
+use super::{InferCtxt, MiscVariable, TypeTrace};
 use super::lub::Lub;
 use super::sub::Sub;
-use super::InferCtxt;
-use super::{MiscVariable, TypeTrace};
+use super::type_variable::TypeVariableValue;
 
 use hir::def_id::DefId;
 use ty::{IntType, UintType};
@@ -194,7 +194,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
         use self::RelationDir::*;
 
         // Get the actual variable that b_vid has been inferred to
-        debug_assert!(self.infcx.type_variables.borrow_mut().probe(b_vid).is_none());
+        debug_assert!(self.infcx.type_variables.borrow_mut().probe(b_vid).is_unknown());
 
         debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", a_ty, dir, b_vid);
 
@@ -389,11 +389,11 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                     return Err(TypeError::CyclicTy);
                 } else {
                     match variables.probe(vid) {
-                        Some(u) => {
+                        TypeVariableValue::Known { value: u } => {
                             drop(variables);
                             self.relate(&u, &u)
                         }
-                        None => {
+                        TypeVariableValue::Unknown { .. } => {
                             match self.ambient_variance {
                                 // Invariant: no need to make a fresh type variable.
                                 ty::Invariant => return Ok(t),
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index 31f4bff6a0c0e..468bfb514262a 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -212,7 +212,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
 
         match t.sty {
             ty::TyInfer(ty::TyVar(v)) => {
-                let opt_ty = self.infcx.type_variables.borrow_mut().probe(v);
+                let opt_ty = self.infcx.type_variables.borrow_mut().probe(v).known();
                 self.freshen(
                     opt_ty,
                     ty::TyVar(v),
diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs
index 9cad6ce6f9fad..04a42f1c8253d 100644
--- a/src/librustc/infer/fudge.rs
+++ b/src/librustc/infer/fudge.rs
@@ -131,7 +131,9 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> {
                         // variables to their binding anyhow, we know
                         // that it is unbound, so we can just return
                         // it.
-                        debug_assert!(self.infcx.type_variables.borrow_mut().probe(vid).is_none());
+                        debug_assert!(self.infcx.type_variables.borrow_mut()
+                                      .probe(vid)
+                                      .is_unknown());
                         ty
                     }
 
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 50f1a94b4c9eb..6c09cd086c004 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -1090,9 +1090,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 // so this recursion should always be of very limited
                 // depth.
                 self.type_variables.borrow_mut()
-                    .probe(v)
-                    .map(|t| self.shallow_resolve(t))
-                    .unwrap_or(typ)
+                                   .probe(v)
+                                   .known()
+                                   .map(|t| self.shallow_resolve(t))
+                                   .unwrap_or(typ)
             }
 
             ty::TyInfer(ty::IntVar(v)) => {
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index cda2fdac6f274..0a562c2f320d0 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -75,12 +75,28 @@ struct TypeVariableData {
     diverging: bool
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum TypeVariableValue<'tcx> {
+#[derive(Copy, Clone, Debug)]
+pub enum TypeVariableValue<'tcx> {
     Known { value: Ty<'tcx> },
     Unknown,
 }
 
+impl<'tcx> TypeVariableValue<'tcx> {
+    pub fn known(&self) -> Option<Ty<'tcx>> {
+        match *self {
+            TypeVariableValue::Unknown { .. } => None,
+            TypeVariableValue::Known { value } => Some(value),
+        }
+    }
+
+    pub fn is_unknown(&self) -> bool {
+        match *self {
+            TypeVariableValue::Unknown { .. } => true,
+            TypeVariableValue::Known { .. } => false,
+        }
+    }
+}
+
 pub struct Snapshot<'tcx> {
     /// number of variables at the time of the snapshot
     num_vars: usize,
@@ -121,8 +137,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
     ///
     /// Precondition: neither `a` nor `b` are known.
     pub fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) {
-        debug_assert!(self.probe(a).is_none());
-        debug_assert!(self.probe(b).is_none());
+        debug_assert!(self.probe(a).is_unknown());
+        debug_assert!(self.probe(b).is_unknown());
         self.eq_relations.union(a, b);
         self.sub_relations.union(a, b);
     }
@@ -131,8 +147,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
     ///
     /// Precondition: neither `a` nor `b` are known.
     pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) {
-        debug_assert!(self.probe(a).is_none());
-        debug_assert!(self.probe(b).is_none());
+        debug_assert!(self.probe(a).is_unknown());
+        debug_assert!(self.probe(b).is_unknown());
         self.sub_relations.union(a, b);
     }
 
@@ -141,8 +157,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
     /// Precondition: `vid` must not have been previously instantiated.
     pub fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) {
         let vid = self.root_var(vid);
-        debug_assert!(self.probe(vid).is_none());
-        debug_assert!(self.eq_relations.probe_value(vid) == TypeVariableValue::Unknown,
+        debug_assert!(self.probe(vid).is_unknown());
+        debug_assert!(self.eq_relations.probe_value(vid).is_unknown(),
                       "instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}",
                       vid, ty, self.eq_relations.probe_value(vid));
         self.eq_relations.union_value(vid, TypeVariableValue::Known { value: ty });
@@ -208,12 +224,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
 
     /// Retrieves the type to which `vid` has been instantiated, if
     /// any.
-    pub fn probe(&mut self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
-        let vid = self.root_var(vid);
-        match self.eq_relations.probe_value(vid) {
-            TypeVariableValue::Unknown => None,
-            TypeVariableValue::Known { value } => Some(value)
-        }
+    pub fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> {
+        self.eq_relations.probe_value(vid)
     }
 
     /// If `t` is a type-inference variable, and it has been
@@ -223,8 +235,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
         match t.sty {
             ty::TyInfer(ty::TyVar(v)) => {
                 match self.probe(v) {
-                    None => t,
-                    Some(u) => u
+                    TypeVariableValue::Unknown { .. } => t,
+                    TypeVariableValue::Known { value } => value,
                 }
             }
             _ => t,
@@ -310,12 +322,9 @@ impl<'tcx> TypeVariableTable<'tcx> {
         // use the less efficient algorithm for now.
         let mut escaping_types = Vec::with_capacity(snapshot.num_vars);
         escaping_types.extend(
-            (0..snapshot.num_vars) // for all variables that pre-exist the snapshot...
+            (0..snapshot.num_vars) // for all variables that pre-exist the snapshot, collect..
                 .map(|i| ty::TyVid { index: i as u32 })
-                .filter_map(|vid| match self.eq_relations.probe_value(vid) {
-                    TypeVariableValue::Unknown => None,
-                    TypeVariableValue::Known { value } => Some(value),
-                })); // ...collect what types they've been instantiated with.
+                .filter_map(|vid| self.probe(vid).known())); // ..types they are instantiated with.
         debug!("types_escaping_snapshot = {:?}", escaping_types);
         escaping_types
     }
@@ -326,10 +335,9 @@ impl<'tcx> TypeVariableTable<'tcx> {
         (0..self.var_data.len())
             .filter_map(|i| {
                 let vid = ty::TyVid { index: i as u32 };
-                if self.probe(vid).is_some() {
-                    None
-                } else {
-                    Some(vid)
+                match self.probe(vid) {
+                    TypeVariableValue::Unknown { .. } => Some(vid),
+                    TypeVariableValue::Known { .. } => None,
                 }
             })
             .collect()

From 64918a2d42675f9302184ef59f67b86b7da1f9d4 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 16 Jul 2017 13:32:34 -0400
Subject: [PATCH 12/63] remove unnecessary clause propagating divergence

This should not be needed: the new variable will be related to the old
ones, so if they are constrained, so is the new variable; if they are
not, and hence default to diverging, so will the new variable.
---
 src/librustc/infer/lattice.rs | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/src/librustc/infer/lattice.rs b/src/librustc/infer/lattice.rs
index d5c1163cfc1b1..28aba51ab3724 100644
--- a/src/librustc/infer/lattice.rs
+++ b/src/librustc/infer/lattice.rs
@@ -70,14 +70,6 @@ pub fn super_lattice_tys<'a, 'gcx, 'tcx, L>(this: &mut L,
     let a = infcx.type_variables.borrow_mut().replace_if_possible(a);
     let b = infcx.type_variables.borrow_mut().replace_if_possible(b);
     match (&a.sty, &b.sty) {
-        (&ty::TyInfer(TyVar(..)), &ty::TyInfer(TyVar(..)))
-            if infcx.type_var_diverges(a) && infcx.type_var_diverges(b) => {
-            let v = infcx.next_diverging_ty_var(
-                TypeVariableOrigin::LatticeVariable(this.cause().span));
-            this.relate_bound(v, a, b)?;
-            Ok(v)
-        }
-
         // If one side is known to be a variable and one is not,
         // create a variable (`v`) to represent the LUB. Make sure to
         // relate `v` to the non-type-variable first (by passing it

From de2dcd6d487698ab499e74185f4ce8387d1134cf Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 17 Jul 2017 16:16:14 -0400
Subject: [PATCH 13/63] add universes to type inference variables

---
 src/librustc/infer/combine.rs               |  4 +--
 src/librustc/infer/fudge.rs                 |  6 +++-
 src/librustc/infer/lattice.rs               | 12 ++++---
 src/librustc/infer/mod.rs                   | 23 +++++++-----
 src/librustc/infer/type_variable.rs         | 30 +++++++++++++---
 src/librustc/traits/coherence.rs            |  4 ++-
 src/librustc/traits/error_reporting.rs      | 17 ++++++---
 src/librustc/traits/project.rs              |  2 ++
 src/librustc/traits/select.rs               |  3 +-
 src/librustc/traits/specialize/mod.rs       |  2 +-
 src/librustc_typeck/check/_match.rs         | 11 ++++--
 src/librustc_typeck/check/closure.rs        |  3 +-
 src/librustc_typeck/check/coercion.rs       |  3 +-
 src/librustc_typeck/check/dropck.rs         |  2 +-
 src/librustc_typeck/check/method/confirm.rs |  4 +--
 src/librustc_typeck/check/method/mod.rs     |  2 +-
 src/librustc_typeck/check/method/probe.rs   |  7 ++--
 src/librustc_typeck/check/method/suggest.rs |  3 +-
 src/librustc_typeck/check/mod.rs            | 40 +++++++++++++--------
 src/librustc_typeck/check/op.rs             |  9 +++--
 20 files changed, 132 insertions(+), 55 deletions(-)

diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 767e7e0e9509a..c0ca157997bf4 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -393,7 +393,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                             drop(variables);
                             self.relate(&u, &u)
                         }
-                        TypeVariableValue::Unknown { .. } => {
+                        TypeVariableValue::Unknown { universe } => {
                             match self.ambient_variance {
                                 // Invariant: no need to make a fresh type variable.
                                 ty::Invariant => return Ok(t),
@@ -410,7 +410,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                             }
 
                             let origin = *variables.var_origin(vid);
-                            let new_var_id = variables.new_var(false, origin);
+                            let new_var_id = variables.new_var(universe, false, origin);
                             let u = self.tcx().mk_var(new_var_id);
                             debug!("generalize: replacing original vid={:?} with new={:?}",
                                    vid, u);
diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs
index 04a42f1c8253d..72f22856304fa 100644
--- a/src/librustc/infer/fudge.rs
+++ b/src/librustc/infer/fudge.rs
@@ -141,7 +141,11 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> {
                         // This variable was created during the
                         // fudging. Recreate it with a fresh variable
                         // here.
-                        self.infcx.next_ty_var(origin)
+                        //
+                        // The ROOT universe is fine because we only
+                        // ever invoke this routine at the
+                        // "item-level" of inference.
+                        self.infcx.next_ty_var(ty::UniverseIndex::ROOT, origin)
                     }
                 }
             }
diff --git a/src/librustc/infer/lattice.rs b/src/librustc/infer/lattice.rs
index 28aba51ab3724..c4722f9a7f96c 100644
--- a/src/librustc/infer/lattice.rs
+++ b/src/librustc/infer/lattice.rs
@@ -88,13 +88,17 @@ pub fn super_lattice_tys<'a, 'gcx, 'tcx, L>(this: &mut L,
         // is (e.g.) `Box<i32>`. A more obvious solution might be to
         // iterate on the subtype obligations that are returned, but I
         // think this suffices. -nmatsakis
-        (&ty::TyInfer(TyVar(..)), _) => {
-            let v = infcx.next_ty_var(TypeVariableOrigin::LatticeVariable(this.cause().span));
+        (&ty::TyInfer(TyVar(a_vid)), _) => {
+            let universe = infcx.type_variables.borrow_mut().probe(a_vid).universe().unwrap();
+            let v = infcx.next_ty_var(universe,
+                                      TypeVariableOrigin::LatticeVariable(this.cause().span));
             this.relate_bound(v, b, a)?;
             Ok(v)
         }
-        (_, &ty::TyInfer(TyVar(..))) => {
-            let v = infcx.next_ty_var(TypeVariableOrigin::LatticeVariable(this.cause().span));
+        (_, &ty::TyInfer(TyVar(b_vid))) => {
+            let universe = infcx.type_variables.borrow_mut().probe(b_vid).universe().unwrap();
+            let v = infcx.next_ty_var(universe,
+                                      TypeVariableOrigin::LatticeVariable(this.cause().span));
             this.relate_bound(v, a, b)?;
             Ok(v)
         }
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 6c09cd086c004..a498f94daa9b5 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -942,18 +942,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         })
     }
 
-    pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
+    pub fn next_ty_var_id(&self,
+                          universe: ty::UniverseIndex,
+                          diverging: bool,
+                          origin: TypeVariableOrigin)
+                          -> TyVid {
         self.type_variables
             .borrow_mut()
-            .new_var(diverging, origin)
+            .new_var(universe, diverging, origin)
     }
 
-    pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
-        self.tcx.mk_var(self.next_ty_var_id(false, origin))
+    pub fn next_ty_var(&self, universe: ty::UniverseIndex, origin: TypeVariableOrigin) -> Ty<'tcx> {
+        self.tcx.mk_var(self.next_ty_var_id(universe, false, origin))
     }
 
-    pub fn next_diverging_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
-        self.tcx.mk_var(self.next_ty_var_id(true, origin))
+    pub fn next_diverging_ty_var(&self, universe: ty::UniverseIndex, origin: TypeVariableOrigin) -> Ty<'tcx> {
+        self.tcx.mk_var(self.next_ty_var_id(universe, true, origin))
     }
 
     pub fn next_int_var_id(&self) -> IntVid {
@@ -991,12 +995,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// use an inference variable for `C` with `[T, U]`
     /// as the substitutions for the default, `(T, U)`.
     pub fn type_var_for_def(&self,
+                            universe: ty::UniverseIndex,
                             span: Span,
                             def: &ty::TypeParameterDef)
                             -> Ty<'tcx> {
         let ty_var_id = self.type_variables
                             .borrow_mut()
-                            .new_var(false,
+                            .new_var(universe,
+                                     false,
                                      TypeVariableOrigin::TypeParameterDefinition(span, def.name));
 
         self.tcx.mk_var(ty_var_id)
@@ -1005,13 +1011,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// Given a set of generics defined on a type or impl, returns a substitution mapping each
     /// type/region parameter to a fresh inference variable.
     pub fn fresh_substs_for_item(&self,
+                                 universe: ty::UniverseIndex,
                                  span: Span,
                                  def_id: DefId)
                                  -> &'tcx Substs<'tcx> {
         Substs::for_item(self.tcx, def_id, |def, _| {
             self.region_var_for_def(span, def)
         }, |def, _| {
-            self.type_var_for_def(span, def)
+            self.type_var_for_def(universe, span, def)
         })
     }
 
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index 0a562c2f320d0..4ee23d2647fa2 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -12,6 +12,7 @@ use syntax::ast;
 use syntax_pos::Span;
 use ty::{self, Ty};
 
+use std::cmp;
 use std::marker::PhantomData;
 use std::u32;
 use rustc_data_structures::fx::FxHashMap;
@@ -78,10 +79,18 @@ struct TypeVariableData {
 #[derive(Copy, Clone, Debug)]
 pub enum TypeVariableValue<'tcx> {
     Known { value: Ty<'tcx> },
-    Unknown,
+    Unknown { universe: ty::UniverseIndex },
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum ProbeTyValue<'tcx> {
+    Ty(Ty<'tcx>),
+    Vid(ty::TyVid),
 }
 
 impl<'tcx> TypeVariableValue<'tcx> {
+    /// If this value is known, returns the type it is known to be.
+    /// Otherwise, `None`.
     pub fn known(&self) -> Option<Ty<'tcx>> {
         match *self {
             TypeVariableValue::Unknown { .. } => None,
@@ -89,6 +98,14 @@ impl<'tcx> TypeVariableValue<'tcx> {
         }
     }
 
+    /// If this value is unknown, returns the universe, otherwise `None`.
+    pub fn universe(&self) -> Option<ty::UniverseIndex> {
+        match *self {
+            TypeVariableValue::Unknown { universe } => Some(universe),
+            TypeVariableValue::Known { .. } => None,
+        }
+    }
+
     pub fn is_unknown(&self) -> bool {
         match *self {
             TypeVariableValue::Unknown { .. } => true,
@@ -175,10 +192,11 @@ impl<'tcx> TypeVariableTable<'tcx> {
     ///   The code in this module doesn't care, but it can be useful
     ///   for improving error messages.
     pub fn new_var(&mut self,
+                   universe: ty::UniverseIndex,
                    diverging: bool,
                    origin: TypeVariableOrigin)
                    -> ty::TyVid {
-        let eq_key = self.eq_relations.new_key(TypeVariableValue::Unknown);
+        let eq_key = self.eq_relations.new_key(TypeVariableValue::Unknown { universe });
 
         let sub_key = self.sub_relations.new_key(());
         assert_eq!(eq_key.vid, sub_key);
@@ -385,8 +403,12 @@ impl<'tcx> ut::UnifyValue for TypeVariableValue<'tcx> {
             (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => Ok(*value1),
             (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => Ok(*value2),
 
-            // If both sides are *unknown*, it hardly matters, does it?
-            (&TypeVariableValue::Unknown, &TypeVariableValue::Unknown) => Ok(*value1),
+            // If both sides are unknown, we need to pick the most restrictive universe.
+            (&TypeVariableValue::Unknown { universe: universe1 },
+             &TypeVariableValue::Unknown { universe: universe2 }) => {
+                let universe = cmp::min(universe1, universe2);
+                Ok(TypeVariableValue::Unknown { universe })
+            }
         }
     }
 }
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs
index 10a32c26e741d..bc805e85f7baa 100644
--- a/src/librustc/traits/coherence.rs
+++ b/src/librustc/traits/coherence.rs
@@ -50,7 +50,9 @@ fn with_fresh_ty_vars<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, '
                                        -> ty::ImplHeader<'tcx>
 {
     let tcx = selcx.tcx();
-    let impl_substs = selcx.infcx().fresh_substs_for_item(DUMMY_SP, impl_def_id);
+    let impl_substs = selcx.infcx().fresh_substs_for_item(param_env.universe,
+                                                          DUMMY_SP,
+                                                          impl_def_id);
 
     let header = ty::ImplHeader {
         impl_def_id,
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index e2b23c12cf1f3..e4e53a6692751 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -285,7 +285,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         self.tcx.for_each_relevant_impl(
             trait_ref.def_id, trait_self_ty, |def_id| {
-                let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
+                let impl_substs = self.fresh_substs_for_item(param_env.universe,
+                                                             obligation.cause.span,
+                                                             def_id);
                 let impl_trait_ref = tcx
                     .impl_trait_ref(def_id)
                     .unwrap()
@@ -1134,6 +1136,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                            -> bool {
         struct ParamToVarFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
             infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+            param_env: ty::ParamEnv<'tcx>,
             var_map: FxHashMap<Ty<'tcx>, Ty<'tcx>>
         }
 
@@ -1143,9 +1146,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
                 if let ty::TyParam(ty::ParamTy {name, ..}) = ty.sty {
                     let infcx = self.infcx;
-                    self.var_map.entry(ty).or_insert_with(||
-                        infcx.next_ty_var(
-                            TypeVariableOrigin::TypeParameterDefinition(DUMMY_SP, name)))
+                    let param_env = self.param_env;
+                    self.var_map
+                        .entry(ty)
+                        .or_insert_with(|| {
+                            let origin = TypeVariableOrigin::TypeParameterDefinition(DUMMY_SP,
+                                                                                     name);
+                            infcx.next_ty_var(param_env.universe, origin)
+                        })
                 } else {
                     ty.super_fold_with(self)
                 }
@@ -1157,6 +1165,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
             let cleaned_pred = pred.fold_with(&mut ParamToVarFolder {
                 infcx: self,
+                param_env,
                 var_map: FxHashMap()
             });
 
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 9c56df058c3dd..6cb556cc99aa5 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -404,6 +404,7 @@ pub fn normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
             let tcx = selcx.infcx().tcx;
             let def_id = projection_ty.item_def_id;
             let ty_var = selcx.infcx().next_ty_var(
+                param_env.universe,
                 TypeVariableOrigin::NormalizeProjectionType(tcx.def_span(def_id)));
             let projection = ty::Binder(ty::ProjectionPredicate {
                 projection_ty,
@@ -724,6 +725,7 @@ fn normalize_to_error<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tc
     let tcx = selcx.infcx().tcx;
     let def_id = projection_ty.item_def_id;
     let new_value = selcx.infcx().next_ty_var(
+        param_env.universe,
         TypeVariableOrigin::NormalizeProjectionType(tcx.def_span(def_id)));
     Normalized {
         value: new_value,
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 514f251452c78..ec911911a07ba 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -3031,7 +3031,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             snapshot);
         let skol_obligation_trait_ref = skol_obligation.trait_ref;
 
-        let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span,
+        let impl_substs = self.infcx.fresh_substs_for_item(obligation.param_env.universe,
+                                                           obligation.cause.span,
                                                            impl_def_id);
 
         let impl_trait_ref = impl_trait_ref.subst(self.tcx(),
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index d8d0715ff3957..5f2136ef9c8cf 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -217,7 +217,7 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, '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_substs = infcx.fresh_substs_for_item(param_env.universe, DUMMY_SP, target_impl);
     let (target_trait_ref, mut obligations) = impl_trait_ref_and_oblig(selcx,
                                                                        param_env,
                                                                        target_impl,
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 272f13b28030e..2fbdc3c236ede 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -285,6 +285,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 let element_tys_iter = (0..max_len).map(|_| self.next_ty_var(
                     // FIXME: MiscVariable for now, obtaining the span and name information
                     //       from all tuple elements isn't trivial.
+                    ty::UniverseIndex::ROOT,
                     TypeVariableOrigin::TypeInference(pat.span)));
                 let element_tys = tcx.mk_type_list(element_tys_iter);
                 let pat_ty = tcx.mk_ty(ty::TyTuple(element_tys, false));
@@ -295,7 +296,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 pat_ty
             }
             PatKind::Box(ref inner) => {
-                let inner_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(inner.span));
+                let inner_ty = self.next_ty_var(ty::UniverseIndex::ROOT,
+                                                TypeVariableOrigin::TypeInference(inner.span));
                 let uniq_ty = tcx.mk_box(inner_ty);
 
                 if self.check_dereferencable(pat.span, expected, &inner) {
@@ -328,6 +330,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         }
                         _ => {
                             let inner_ty = self.next_ty_var(
+                                ty::UniverseIndex::ROOT,
                                 TypeVariableOrigin::TypeInference(inner.span));
                             let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl };
                             let region = self.next_region_var(infer::PatternRegion(pat.span));
@@ -571,7 +574,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             // ...but otherwise we want to use any supertype of the
             // discriminant. This is sort of a workaround, see note (*) in
             // `check_pat` for some details.
-            discrim_ty = self.next_ty_var(TypeVariableOrigin::TypeInference(discrim.span));
+            discrim_ty = self.next_ty_var(ty::UniverseIndex::ROOT,
+                                          TypeVariableOrigin::TypeInference(discrim.span));
             self.check_expr_has_type_or_error(discrim, discrim_ty);
         };
 
@@ -632,7 +636,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // arm for inconsistent arms or to the whole match when a `()` type
                 // is required).
                 Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => ety,
-                _ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
+                _ => self.next_ty_var(ty::UniverseIndex::ROOT,
+                                      TypeVariableOrigin::MiscVariable(expr.span)),
             };
             CoerceMany::with_coercion_sites(coerce_first, arms)
         };
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index d475fb0cf1a14..f7a086c160b64 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -100,7 +100,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             |_, _| span_bug!(expr.span, "closure has region param"),
             |_, _| {
                 self.infcx
-                    .next_ty_var(TypeVariableOrigin::TransformedUpvar(expr.span))
+                    .next_ty_var(ty::UniverseIndex::ROOT,
+                                 TypeVariableOrigin::TransformedUpvar(expr.span))
             },
         );
         let closure_type = self.tcx.mk_closure(expr_def_id, substs);
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 94422f93e5922..3941b3c8f1b5a 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -179,6 +179,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                 // micro-optimization: no need for this if `b` is
                 // already resolved in some way.
                 let diverging_ty = self.next_diverging_ty_var(
+                    ty::UniverseIndex::ROOT,
                     TypeVariableOrigin::AdjustmentType(self.cause.span));
                 self.unify_and(&b, &diverging_ty, simple(Adjust::NeverToAny))
             } else {
@@ -497,7 +498,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         // We only have the latter, so we use an inference variable
         // for the former and let type inference do the rest.
         let origin = TypeVariableOrigin::MiscVariable(self.cause.span);
-        let coerce_target = self.next_ty_var(origin);
+        let coerce_target = self.next_ty_var(ty::UniverseIndex::ROOT, origin);
         let mut coercion = self.unify_and(coerce_target, target, |target| {
             let unsize = Adjustment {
                 kind: Adjust::Unsize,
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 610d07efa359d..0a5f3c76aec98 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -88,7 +88,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
 
         let drop_impl_span = tcx.def_span(drop_impl_did);
         let fresh_impl_substs =
-            infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did);
+            infcx.fresh_substs_for_item(ty::UniverseIndex::ROOT, drop_impl_span, drop_impl_did);
         let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs);
 
         let cause = &ObligationCause::misc(drop_impl_span, drop_impl_node_id);
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 5f303a055493d..b6b39a807563a 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -240,7 +240,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
                 // the process we will unify the transformed-self-type
                 // of the method with the actual type in order to
                 // unify some of these variables.
-                self.fresh_substs_for_item(self.span, trait_def_id)
+                self.fresh_substs_for_item(ty::UniverseIndex::ROOT, self.span, trait_def_id)
             }
 
             probe::WhereClausePick(ref poly_trait_ref) => {
@@ -316,7 +316,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
             {
                 self.to_ty(ast_ty)
             } else {
-                self.type_var_for_def(self.span, def)
+                self.type_var_for_def(ty::UniverseIndex::ROOT, self.span, def)
             }
         })
     }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 4a122fbc4c195..af86570309dfd 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -255,7 +255,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             } else if let Some(ref input_types) = opt_input_types {
                 input_types[def.index as usize - 1]
             } else {
-                self.type_var_for_def(span, def)
+                self.type_var_for_def(ty::UniverseIndex::ROOT, span, def)
             }
         });
 
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index ce3c617f43444..646867283e23b 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -693,7 +693,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             Def::Method(def_id) => {
                 let fty = self.tcx.fn_sig(def_id);
                 self.probe(|_| {
-                    let substs = self.fresh_substs_for_item(self.span, method.def_id);
+                    let substs = self.fresh_substs_for_item(ty::UniverseIndex::ROOT,
+                                                            self.span,
+                                                            method.def_id);
                     let fty = fty.subst(self.tcx, substs);
                     let (fty, _) = self.replace_late_bound_regions_with_fresh_var(
                         self.span, infer::FnCall, &fty);
@@ -1270,7 +1272,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                 if i < substs.len() {
                     substs.type_at(i)
                 } else {
-                    self.type_var_for_def(self.span, def)
+                    self.type_var_for_def(ty::UniverseIndex::ROOT, self.span, def)
                 }
             });
             xform_fn_sig.subst(self.tcx, substs)
@@ -1287,6 +1289,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                          def_id,
                          |_, _| self.tcx.types.re_erased,
                          |_, _| self.next_ty_var(
+                             ty::UniverseIndex::ROOT,
                              TypeVariableOrigin::SubstitutionPlaceholder(
                                  self.tcx.def_span(def_id))))
     }
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 8613ec86b4a73..bdb56b7da94e6 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -54,7 +54,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 self.autoderef(span, ty).any(|(ty, _)| {
                     self.probe(|_| {
                         let fn_once_substs = tcx.mk_substs_trait(ty,
-                            &[self.next_ty_var(TypeVariableOrigin::MiscVariable(span))]);
+                            &[self.next_ty_var(ty::UniverseIndex::ROOT,
+                                               TypeVariableOrigin::MiscVariable(span))]);
                         let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs);
                         let poly_trait_ref = trait_ref.to_poly_trait_ref();
                         let obligation =
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 71899a573801e..a1f2a0f3a5461 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -360,7 +360,8 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
     /// hard constraint exists, creates a fresh type variable.
     fn coercion_target_type(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, span: Span) -> Ty<'tcx> {
         self.only_has_type(fcx)
-            .unwrap_or_else(|| fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span)))
+            .unwrap_or_else(|| fcx.next_ty_var(ty::UniverseIndex::ROOT,
+                                               TypeVariableOrigin::MiscVariable(span)))
     }
 }
 
@@ -945,7 +946,8 @@ impl<'a, 'gcx, 'tcx> GatherLocalsVisitor<'a, 'gcx, 'tcx> {
         match ty_opt {
             None => {
                 // infer the variable's type
-                let var_ty = self.fcx.next_ty_var(TypeVariableOrigin::TypeInference(span));
+                let var_ty = self.fcx.next_ty_var(ty::UniverseIndex::ROOT,
+                                                  TypeVariableOrigin::TypeInference(span));
                 self.fcx.locals.borrow_mut().insert(nid, var_ty);
                 var_ty
             }
@@ -1038,7 +1040,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
     let span = body.value.span;
 
     if body.is_generator && can_be_generator {
-        fcx.yield_ty = Some(fcx.next_ty_var(TypeVariableOrigin::TypeInference(span)));
+        fcx.yield_ty = Some(fcx.next_ty_var(ty::UniverseIndex::ROOT,
+                                            TypeVariableOrigin::TypeInference(span)));
     }
 
     GatherLocalsVisitor { fcx: &fcx, }.visit_body(body);
@@ -1068,7 +1071,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
         };
         inherited.tables.borrow_mut().generator_sigs_mut().insert(fn_hir_id, Some(gen_sig));
 
-        let witness = fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span));
+        let witness = fcx.next_ty_var(ty::UniverseIndex::ROOT,
+                                      TypeVariableOrigin::MiscVariable(span));
         fcx.deferred_generator_interiors.borrow_mut().push((body.id(), witness));
         let interior = ty::GeneratorInterior::new(witness);
 
@@ -1110,6 +1114,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
     let mut actual_return_ty = coercion.complete(&fcx);
     if actual_return_ty.is_never() {
         actual_return_ty = fcx.next_diverging_ty_var(
+            ty::UniverseIndex::ROOT,
             TypeVariableOrigin::DivergingFn(span));
     }
     fcx.demand_suptype(span, ret_ty, actual_return_ty);
@@ -1647,13 +1652,14 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> {
     }
 
     fn ty_infer(&self, span: Span) -> Ty<'tcx> {
-        self.next_ty_var(TypeVariableOrigin::TypeInference(span))
+        self.next_ty_var(ty::UniverseIndex::ROOT,
+                         TypeVariableOrigin::TypeInference(span))
     }
 
     fn ty_infer_for_def(&self,
                         ty_param_def: &ty::TypeParameterDef,
                         span: Span) -> Ty<'tcx> {
-        self.type_var_for_def(span, ty_param_def)
+        self.type_var_for_def(ty::UniverseIndex::ROOT, span, ty_param_def)
     }
 
     fn projected_ty_from_poly_trait_ref(&self,
@@ -1944,7 +1950,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     return ty_var;
                 }
                 let span = self.tcx.def_span(def_id);
-                let ty_var = self.next_ty_var(TypeVariableOrigin::TypeInference(span));
+                let ty_var = self.next_ty_var(ty::UniverseIndex::ROOT,
+                                              TypeVariableOrigin::TypeInference(span));
                 self.anon_types.borrow_mut().insert(id, ty_var);
 
                 let predicates_of = self.tcx.predicates_of(def_id);
@@ -2278,7 +2285,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             // If some lookup succeeds, write callee into table and extract index/element
             // type from the method signature.
             // If some lookup succeeded, install method in table
-            let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_expr.span));
+            let input_ty = self.next_ty_var(ty::UniverseIndex::ROOT,
+                                            TypeVariableOrigin::AutoDeref(base_expr.span));
             let method = self.try_overloaded_lvalue_op(
                 expr.span, self_ty, &[input_ty], lvalue_pref, LvalueOp::Index);
 
@@ -2722,6 +2730,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             assert!(!self.tables.borrow().adjustments().contains_key(expr.hir_id),
                     "expression with never type wound up being adjusted");
             let adj_ty = self.next_diverging_ty_var(
+                ty::UniverseIndex::ROOT,
                 TypeVariableOrigin::AdjustmentType(expr.span));
             self.apply_adjustments(expr, vec![Adjustment {
                 kind: Adjust::NeverToAny,
@@ -2800,7 +2809,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let ity = self.tcx.type_of(did);
         debug!("impl_self_ty: ity={:?}", ity);
 
-        let substs = self.fresh_substs_for_item(span, did);
+        let substs = self.fresh_substs_for_item(ty::UniverseIndex::ROOT, span, did);
         let substd_ty = self.instantiate_type_scheme(span, &substs, &ity);
 
         TypeAndSubsts { substs: substs, ty: substd_ty }
@@ -3931,7 +3940,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
               let element_ty = if !args.is_empty() {
                   let coerce_to = uty.unwrap_or_else(
-                      || self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span)));
+                      || self.next_ty_var(ty::UniverseIndex::ROOT,
+                                          TypeVariableOrigin::TypeInference(expr.span)));
                   let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
                   assert_eq!(self.diverges.get(), Diverges::Maybe);
                   for e in args {
@@ -3941,7 +3951,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                   }
                   coerce.complete(self)
               } else {
-                  self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span))
+                  self.next_ty_var(ty::UniverseIndex::ROOT,
+                                   TypeVariableOrigin::TypeInference(expr.span))
               };
               tcx.mk_array(element_ty, args.len() as u64)
           }
@@ -3971,7 +3982,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     (uty, uty)
                 }
                 None => {
-                    let t: Ty = self.next_ty_var(TypeVariableOrigin::MiscVariable(element.span));
+                    let t: Ty = self.next_ty_var(ty::UniverseIndex::ROOT,
+                                                 TypeVariableOrigin::MiscVariable(element.span));
                     let element_ty = self.check_expr_has_type_or_error(&element, t);
                     (element_ty, t)
                 }
@@ -4746,7 +4758,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // Handle Self first, so we can adjust the index to match the AST.
                 if has_self && i == 0 {
                     return opt_self_ty.unwrap_or_else(|| {
-                        self.type_var_for_def(span, def)
+                        self.type_var_for_def(ty::UniverseIndex::ROOT, span, def)
                     });
                 }
                 i -= has_self as usize;
@@ -4779,7 +4791,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // This can also be reached in some error cases:
                 // We prefer to use inference variables instead of
                 // TyError to let type inference recover somewhat.
-                self.type_var_for_def(span, def)
+                self.type_var_for_def(ty::UniverseIndex::ROOT, span, def)
             }
         });
 
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 2d45f797ecb4d..987fead48c474 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -175,8 +175,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // trait matching creating lifetime constraints that are too strict.
         // E.g. adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
         // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
-        let lhs_ty = self.check_expr_coercable_to_type_with_lvalue_pref(lhs_expr,
-            self.next_ty_var(TypeVariableOrigin::MiscVariable(lhs_expr.span)),
+        let lhs_ty = self.check_expr_coercable_to_type_with_lvalue_pref(
+            lhs_expr,
+            self.next_ty_var(ty::UniverseIndex::ROOT,
+                             TypeVariableOrigin::MiscVariable(lhs_expr.span)),
             lhs_pref);
         let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty);
 
@@ -186,7 +188,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // using this variable as the expected type, which sometimes lets
         // us do better coercions than we would be able to do otherwise,
         // particularly for things like `String + &String`.
-        let rhs_ty_var = self.next_ty_var(TypeVariableOrigin::MiscVariable(rhs_expr.span));
+        let rhs_ty_var = self.next_ty_var(ty::UniverseIndex::ROOT,
+                         TypeVariableOrigin::MiscVariable(rhs_expr.span));
 
         let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign));
 

From 12a230562ece9b0d29018a436676141054dc53b7 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 31 Jul 2017 07:40:24 +0300
Subject: [PATCH 14/63] change skolemizations to use universe index

---
 src/librustc/infer/region_inference/mod.rs | 34 +++++++++++-----------
 src/librustc/ty/mod.rs                     | 18 ++++++++++--
 src/librustc/ty/sty.rs                     |  7 +----
 src/librustc/util/ppaux.rs                 |  2 +-
 4 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 5db162848bfe9..b197344bda914 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -216,7 +216,7 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     lubs: RefCell<CombineMap<'tcx>>,
     glbs: RefCell<CombineMap<'tcx>>,
-    skolemization_count: Cell<u32>,
+    skolemization_count: Cell<ty::UniverseIndex>,
     bound_count: Cell<u32>,
 
     /// The undo log records actions that might later be undone.
@@ -240,7 +240,7 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 pub struct RegionSnapshot {
     length: usize,
     region_snapshot: ut::Snapshot<ut::InPlace<ty::RegionVid>>,
-    skolemization_count: u32,
+    skolemization_count: ty::UniverseIndex,
 }
 
 /// When working with skolemized regions, we often wish to find all of
@@ -362,7 +362,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             givens: RefCell::new(FxHashSet()),
             lubs: RefCell::new(FxHashMap()),
             glbs: RefCell::new(FxHashMap()),
-            skolemization_count: Cell::new(0),
+            skolemization_count: Cell::new(ty::UniverseIndex::ROOT),
             bound_count: Cell::new(0),
             undo_log: RefCell::new(Vec::new()),
             unification_table: RefCell::new(ut::UnificationTable::new()),
@@ -389,7 +389,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         assert!(self.undo_log.borrow().len() > snapshot.length);
         assert!((*self.undo_log.borrow())[snapshot.length] == OpenSnapshot);
         assert!(self.skolemization_count.get() == snapshot.skolemization_count,
-                "failed to pop skolemized regions: {} now vs {} at start",
+                "failed to pop skolemized regions: {:?} now vs {:?} at start",
                 self.skolemization_count.get(),
                 snapshot.skolemization_count);
 
@@ -501,9 +501,9 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         assert!(self.in_snapshot());
         assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot);
 
-        let sc = self.skolemization_count.get();
-        self.skolemization_count.set(sc + 1);
-        self.tcx.mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br))
+        let universe = self.skolemization_count.get().subuniverse();
+        self.skolemization_count.set(universe);
+        self.tcx.mk_region(ReSkolemized(universe, br))
     }
 
     /// Removes all the edges to/from the skolemized regions that are
@@ -517,31 +517,31 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
         assert!(self.in_snapshot());
         assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot);
-        assert!(self.skolemization_count.get() as usize >= skols.len(),
+        assert!(self.skolemization_count.get().as_usize() >= skols.len(),
                 "popping more skolemized variables than actually exist, \
                  sc now = {}, skols.len = {}",
-                self.skolemization_count.get(),
+                self.skolemization_count.get().as_usize(),
                 skols.len());
 
-        let last_to_pop = self.skolemization_count.get();
-        let first_to_pop = last_to_pop - (skols.len() as u32);
+        let last_to_pop = self.skolemization_count.get().subuniverse();
+        let first_to_pop = ty::UniverseIndex::from(last_to_pop.as_u32() - (skols.len() as u32));
 
         assert!(first_to_pop >= snapshot.skolemization_count,
                 "popping more regions than snapshot contains, \
-                 sc now = {}, sc then = {}, skols.len = {}",
+                 sc now = {:?}, sc then = {:?}, skols.len = {}",
                 self.skolemization_count.get(),
                 snapshot.skolemization_count,
                 skols.len());
         debug_assert! {
             skols.iter()
                  .all(|&k| match *k {
-                     ty::ReSkolemized(index, _) =>
-                         index.index >= first_to_pop &&
-                         index.index < last_to_pop,
+                     ty::ReSkolemized(universe, _) =>
+                         universe >= first_to_pop &&
+                         universe < last_to_pop,
                      _ =>
                          false
                  }),
-            "invalid skolemization keys or keys out of range ({}..{}): {:?}",
+            "invalid skolemization keys or keys out of range ({:?}..{:?}): {:?}",
             snapshot.skolemization_count,
             self.skolemization_count.get(),
             skols
@@ -1523,7 +1523,7 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
 
 impl fmt::Debug for RegionSnapshot {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "RegionSnapshot(length={},skolemization={})",
+        write!(f, "RegionSnapshot(length={},skolemization={:?})",
                self.length, self.skolemization_count)
     }
 }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 8fbc14023ce66..af2bb1b1e7fab 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -68,7 +68,7 @@ pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef};
 pub use self::sty::{ExistentialProjection, PolyExistentialProjection, Const};
 pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region};
 pub use self::sty::RegionKind;
-pub use self::sty::{TyVid, IntVid, FloatVid, RegionVid, SkolemizedRegionVid};
+pub use self::sty::{TyVid, IntVid, FloatVid, RegionVid};
 pub use self::sty::BoundRegion::*;
 pub use self::sty::InferTy::*;
 pub use self::sty::RegionKind::*;
@@ -1275,7 +1275,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
 /// type name in a non-zero universe is a skolemized type -- an
 /// idealized representative of "types in general" that we use for
 /// checking generic functions.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub struct UniverseIndex(u32);
 
 impl UniverseIndex {
@@ -1295,7 +1295,19 @@ impl UniverseIndex {
     /// region `'a`, but that region was not nameable from `U` because
     /// it was not in scope there.
     pub fn subuniverse(self) -> UniverseIndex {
-        UniverseIndex(self.0 + 1)
+        UniverseIndex(self.0.checked_add(1).unwrap())
+    }
+
+    pub fn from(v: u32) -> UniverseIndex {
+        UniverseIndex(v)
+    }
+
+    pub fn as_u32(&self) -> u32 {
+        self.0
+    }
+
+    pub fn as_usize(&self) -> usize {
+        self.0 as usize
     }
 
     /// Gets the "depth" of this universe in the universe tree. This
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index d0ac7d0183a58..c1328552ccb4e 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -854,7 +854,7 @@ pub enum RegionKind {
 
     /// A skolemized region - basically the higher-ranked version of ReFree.
     /// Should not exist after typeck.
-    ReSkolemized(SkolemizedRegionVid, BoundRegion),
+    ReSkolemized(ty::UniverseIndex, BoundRegion),
 
     /// Empty lifetime is for data that is never accessed.
     /// Bottom in the region lattice. We treat ReEmpty somewhat
@@ -898,11 +898,6 @@ pub struct RegionVid {
     pub index: u32,
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
-pub struct SkolemizedRegionVid {
-    pub index: u32,
-}
-
 #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum InferTy {
     TyVar(TyVid),
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 73dfc01718eb1..ba147443a71dc 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -762,7 +762,7 @@ define_print! {
                 }
 
                 ty::ReSkolemized(id, ref bound_region) => {
-                    write!(f, "ReSkolemized({}, {:?})", id.index, bound_region)
+                    write!(f, "ReSkolemized({:?}, {:?})", id, bound_region)
                 }
 
                 ty::ReEmpty => write!(f, "ReEmpty"),

From 3cee5c275cfcd84f8b6ba026b53dd3fe80af80ac Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Tue, 31 Oct 2017 11:40:24 -0400
Subject: [PATCH 15/63] fix tidy error

---
 src/librustc/infer/mod.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index a498f94daa9b5..f411ad5b29602 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -956,7 +956,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.tcx.mk_var(self.next_ty_var_id(universe, false, origin))
     }
 
-    pub fn next_diverging_ty_var(&self, universe: ty::UniverseIndex, origin: TypeVariableOrigin) -> Ty<'tcx> {
+    pub fn next_diverging_ty_var(&self,
+                                 universe: ty::UniverseIndex,
+                                 origin: TypeVariableOrigin)
+                                 -> Ty<'tcx> {
         self.tcx.mk_var(self.next_ty_var_id(universe, true, origin))
     }
 

From edff81141ff40e733ac2881b2a88bc7005a047c5 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Tue, 31 Oct 2017 15:24:55 -0400
Subject: [PATCH 16/63] fix tests in `librustc_driver`

---
 src/librustc_driver/test.rs | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 5ff75351b635b..201b6e40b0426 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -503,7 +503,8 @@ fn sub_free_bound_false_infer() {
     //! does NOT hold for any instantiation of `_#1`.
 
     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_infer1 = env.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
+        let t_infer1 = env.infcx.next_ty_var(ty::UniverseIndex::ROOT,
+                                             TypeVariableOrigin::MiscVariable(DUMMY_SP));
         let t_rptr_bound1 = env.t_rptr_late_bound(1);
         env.check_not_sub(env.t_fn(&[t_infer1], env.tcx().types.isize),
                           env.t_fn(&[t_rptr_bound1], env.tcx().types.isize));
@@ -522,7 +523,8 @@ fn lub_free_bound_infer() {
 
     test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| {
         env.create_simple_region_hierarchy();
-        let t_infer1 = env.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
+        let t_infer1 = env.infcx.next_ty_var(ty::UniverseIndex::ROOT,
+                                             TypeVariableOrigin::MiscVariable(DUMMY_SP));
         let t_rptr_bound1 = env.t_rptr_late_bound(1);
         let t_rptr_free1 = env.t_rptr_free(1);
         env.check_lub(env.t_fn(&[t_infer1], env.tcx().types.isize),
@@ -642,7 +644,8 @@ fn glb_bound_free() {
 fn glb_bound_free_infer() {
     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
         let t_rptr_bound1 = env.t_rptr_late_bound(1);
-        let t_infer1 = env.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
+        let t_infer1 = env.infcx.next_ty_var(ty::UniverseIndex::ROOT,
+                                             TypeVariableOrigin::MiscVariable(DUMMY_SP));
 
         // compute GLB(fn(_) -> isize, for<'b> fn(&'b isize) -> isize),
         // which should yield for<'b> fn(&'b isize) -> isize

From 785c3610f40e09eb2e8ef9836962cef3266c5edf Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 31 Jul 2017 14:36:40 +0300
Subject: [PATCH 17/63] store RegionVariableInfo and not just
 RegionVariableOrigin

---
 src/librustc/infer/region_inference/mod.rs | 35 +++++++++++++---------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index b197344bda914..acc4d13671904 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -179,9 +179,14 @@ pub enum ProcessedErrorOrigin<'tcx> {
 
 pub type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
 
+#[derive(Clone, Debug)]
+struct RegionVariableInfo {
+    origin: RegionVariableOrigin
+}
+
 pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
-    var_origins: RefCell<Vec<RegionVariableOrigin>>,
+    var_infos: RefCell<Vec<RegionVariableInfo>>,
 
     /// Constraints of the form `A <= B` introduced by the region
     /// checker.  Here at least one of `A` and `B` must be a region
@@ -355,7 +360,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> RegionVarBindings<'a, 'gcx, 'tcx> {
         RegionVarBindings {
             tcx,
-            var_origins: RefCell::new(Vec::new()),
+            var_infos: RefCell::new(Vec::new()),
             values: RefCell::new(None),
             constraints: RefCell::new(FxHashMap()),
             verifys: RefCell::new(Vec::new()),
@@ -426,9 +431,9 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                 // nothing to do here
             }
             AddVar(vid) => {
-                let mut var_origins = self.var_origins.borrow_mut();
-                var_origins.pop().unwrap();
-                assert_eq!(var_origins.len(), vid.index as usize);
+                let mut var_infos = self.var_infos.borrow_mut();
+                var_infos.pop().unwrap();
+                assert_eq!(var_infos.len(), vid.index as usize);
             }
             AddConstraint(ref constraint) => {
                 self.constraints.borrow_mut().remove(constraint);
@@ -450,7 +455,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
     }
 
     pub fn num_vars(&self) -> u32 {
-        let len = self.var_origins.borrow().len();
+        let len = self.var_infos.borrow().len();
         // enforce no overflow
         assert!(len as u32 as usize == len);
         len as u32
@@ -458,7 +463,9 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
     pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid {
         let vid = RegionVid { index: self.num_vars() };
-        self.var_origins.borrow_mut().push(origin.clone());
+        self.var_infos.borrow_mut().push(RegionVariableInfo {
+            origin: origin.clone()
+        });
 
         let u_vid = self.unification_table.borrow_mut().new_key(
             unify_key::RegionVidKey { min_vid: vid }
@@ -474,7 +481,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
     }
 
     pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin {
-        self.var_origins.borrow()[vid.index as usize].clone()
+        self.var_infos.borrow()[vid.index as usize].origin.clone()
     }
 
     /// Creates a new skolemized region. Skolemized regions are fresh
@@ -795,7 +802,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
     pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> {
         match *self.values.borrow() {
             None => {
-                span_bug!((*self.var_origins.borrow())[rid.index as usize].span(),
+                span_bug!((*self.var_infos.borrow())[rid.index as usize].origin.span(),
                           "attempt to resolve region variable before values have \
                            been computed!")
             }
@@ -919,7 +926,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             }
 
             (&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
-                span_bug!((*self.var_origins.borrow())[v_id.index as usize].span(),
+                span_bug!((*self.var_infos.borrow())[v_id.index as usize].origin.span(),
                           "lub_concrete_regions invoked with non-concrete \
                            regions: {:?}, {:?}",
                           a,
@@ -1365,14 +1372,14 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         for lower_bound in &lower_bounds {
             for upper_bound in &upper_bounds {
                 if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) {
-                    let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone();
+                    let info = (*self.var_infos.borrow())[node_idx.index as usize].clone();
                     debug!("region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
                             sup: {:?}",
-                           origin,
+                           info.origin,
                            node_idx,
                            lower_bound.region,
                            upper_bound.region);
-                    errors.push(SubSupConflict(origin,
+                    errors.push(SubSupConflict(info.origin,
                                                lower_bound.origin.clone(),
                                                lower_bound.region,
                                                upper_bound.origin.clone(),
@@ -1382,7 +1389,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             }
         }
 
-        span_bug!((*self.var_origins.borrow())[node_idx.index as usize].span(),
+        span_bug!((*self.var_infos.borrow())[node_idx.index as usize].origin.span(),
                   "collect_error_for_expanding_node() could not find \
                    error for var {:?}, lower_bounds={:?}, \
                    upper_bounds={:?}",

From c3f515bd3cefe88429b35fa96f8cf3017d164f0d Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 16 Aug 2017 10:09:59 -0400
Subject: [PATCH 18/63] give a universe to region variables

---
 src/librustc/infer/README.md                | 299 +++++---------------
 src/librustc/infer/combine.rs               |  11 +-
 src/librustc/infer/fudge.rs                 |   6 +-
 src/librustc/infer/higher_ranked/mod.rs     |  27 +-
 src/librustc/infer/mod.rs                   |  14 +-
 src/librustc/infer/region_inference/mod.rs  |  42 ++-
 src/librustc/traits/error_reporting.rs      |   1 +
 src/librustc_mir/transform/nll/renumber.rs  |   6 +-
 src/librustc_mir/transform/rustc_peek.rs    |   4 +-
 src/librustc_typeck/check/_match.rs         |   6 +-
 src/librustc_typeck/check/callee.rs         |  14 +-
 src/librustc_typeck/check/closure.rs        |   2 +
 src/librustc_typeck/check/coercion.rs       |   5 +-
 src/librustc_typeck/check/compare_method.rs |  16 +-
 src/librustc_typeck/check/method/confirm.rs |   9 +-
 src/librustc_typeck/check/method/mod.rs     |   5 +-
 src/librustc_typeck/check/method/probe.rs   |   2 +-
 src/librustc_typeck/check/mod.rs            |   6 +-
 src/librustc_typeck/check/regionck.rs       |   1 +
 src/librustc_typeck/check/upvar.rs          |   2 +-
 20 files changed, 203 insertions(+), 275 deletions(-)

diff --git a/src/librustc/infer/README.md b/src/librustc/infer/README.md
index 6c1478531f147..a528b5d08bb9d 100644
--- a/src/librustc/infer/README.md
+++ b/src/librustc/infer/README.md
@@ -1,239 +1,86 @@
 # Type inference engine
 
-This is loosely based on standard HM-type inference, but with an
-extension to try and accommodate subtyping.  There is nothing
-principled about this extension; it's sound---I hope!---but it's a
-heuristic, ultimately, and does not guarantee that it finds a valid
-typing even if one exists (in fact, there are known scenarios where it
-fails, some of which may eventually become problematic).
-
-## Key idea
-
-The main change is that each type variable T is associated with a
-lower-bound L and an upper-bound U.  L and U begin as bottom and top,
-respectively, but gradually narrow in response to new constraints
-being introduced.  When a variable is finally resolved to a concrete
-type, it can (theoretically) select any type that is a supertype of L
-and a subtype of U.
-
-There are several critical invariants which we maintain:
-
-- the upper-bound of a variable only becomes lower and the lower-bound
-  only becomes higher over time;
-- the lower-bound L is always a subtype of the upper bound U;
-- the lower-bound L and upper-bound U never refer to other type variables,
-  but only to types (though those types may contain type variables).
-
-> An aside: if the terms upper- and lower-bound confuse you, think of
-> "supertype" and "subtype".  The upper-bound is a "supertype"
-> (super=upper in Latin, or something like that anyway) and the lower-bound
-> is a "subtype" (sub=lower in Latin).  I find it helps to visualize
-> a simple class hierarchy, like Java minus interfaces and
-> primitive types.  The class Object is at the root (top) and other
-> types lie in between.  The bottom type is then the Null type.
-> So the tree looks like:
->
-> ```text
->         Object
->         /    \
->     String   Other
->         \    /
->         (null)
-> ```
->
-> So the upper bound type is the "supertype" and the lower bound is the
-> "subtype" (also, super and sub mean upper and lower in Latin, or something
-> like that anyway).
-
-## Satisfying constraints
-
-At a primitive level, there is only one form of constraint that the
-inference understands: a subtype relation.  So the outside world can
-say "make type A a subtype of type B".  If there are variables
-involved, the inferencer will adjust their upper- and lower-bounds as
-needed to ensure that this relation is satisfied. (We also allow "make
-type A equal to type B", but this is translated into "A <: B" and "B
-<: A")
-
-As stated above, we always maintain the invariant that type bounds
-never refer to other variables.  This keeps the inference relatively
-simple, avoiding the scenario of having a kind of graph where we have
-to pump constraints along and reach a fixed point, but it does impose
-some heuristics in the case where the user is relating two type
-variables A <: B.
-
-Combining two variables such that variable A will forever be a subtype
-of variable B is the trickiest part of the algorithm because there is
-often no right choice---that is, the right choice will depend on
-future constraints which we do not yet know. The problem comes about
-because both A and B have bounds that can be adjusted in the future.
-Let's look at some of the cases that can come up.
-
-Imagine, to start, the best case, where both A and B have an upper and
-lower bound (that is, the bounds are not top nor bot respectively). In
-that case, if we're lucky, A.ub <: B.lb, and so we know that whatever
-A and B should become, they will forever have the desired subtyping
-relation.  We can just leave things as they are.
-
-### Option 1: Unify
-
-However, suppose that A.ub is *not* a subtype of B.lb.  In
-that case, we must make a decision.  One option is to unify A
-and B so that they are one variable whose bounds are:
-
-    UB = GLB(A.ub, B.ub)
-    LB = LUB(A.lb, B.lb)
-
-(Note that we will have to verify that LB <: UB; if it does not, the
-types are not intersecting and there is an error) In that case, A <: B
-holds trivially because A==B.  However, we have now lost some
-flexibility, because perhaps the user intended for A and B to end up
-as different types and not the same type.
-
-Pictorially, what this does is to take two distinct variables with
-(hopefully not completely) distinct type ranges and produce one with
-the intersection.
-
-```text
-                  B.ub                  B.ub
-                   /\                    /
-           A.ub   /  \           A.ub   /
-           /   \ /    \              \ /
-          /     X      \              UB
-         /     / \      \            / \
-        /     /   /      \          /   /
-        \     \  /       /          \  /
-         \      X       /             LB
-          \    / \     /             / \
-           \  /   \   /             /   \
-           A.lb    B.lb          A.lb    B.lb
+The engine is based around an extension of HM-type-inference so that
+it supports "multiple universes" (that is, the ability to reason about
+variables where different sets of names are in scope per variable).
+This is very similar to the "Labeled Unification" procedure described
+in the paper,
+["A Proof Procedure for the Logic of Hereditary Harrop Formulas"][pplhhhf]
+by Gopalan Nadathur, with some extensions to accommodate subtyping.
+
+[pplhhhf]: https://pdfs.semanticscholar.org/335f/16ac16b3de99679cb7adf435056000ef811b.pdf
+
+## Universes and skolemization
+
+Universes and skolemization are two complex sound terms, but the
+underlying idea is quite simple. Consider a type like this:
+
+    for<'a> fn(&'a u32)
+    
+Here the `for<'a>` quantifier brings `'a` into scope -- that is,
+within the function type, we can refer to `'a`. But outside, we cannot
+(in fact, Rust does not allow shadowing in such names, but even if it
+did, if we were to use the name `'a`, it would be referring to some
+other lifetime, not `'a`).
+
+To describe this idea, we say that, when we enter the function type,
+we enter into a **subuniverse** U1 of the original universe U0. In
+this subuniverse U1, we can name all the names from U0, but we can
+also use refer to `'a`. In this case, when we refer to `'a`, we are
+not referring to any 'real' region, but rather an abstract one -- a
+kind of ideal representative, meaning "any region that might later be
+used for `'a`". This idea, of an abstract representative, is called a
+**skolemized** region (named after [Thoraf Skolem], a philosopher and
+mathematician who pioneered the technique).
+
+[Thoraf Skolem]: https://en.wikipedia.org/wiki/Thoralf_Skolem
+
+The same ideas (universes and skolemization) apply to types, which
+might help make them easier to understand. Consider:
+
+```rust
+fn foo<T: Display>(v: Vec<T>) { ... }
 ```
 
+When we type-check the body of `foo`, we are able to refer to `T` as
+if it were a real type. But it's not a real type -- it's a *skolemized
+type*, representing the abstract idea of "some type T, where the only
+thing we know about `T` is that it is `Sized` and it implements
+`Display`".
 
-### Option 2: Relate UB/LB
-
-Another option is to keep A and B as distinct variables but set their
-bounds in such a way that, whatever happens, we know that A <: B will hold.
-This can be achieved by ensuring that A.ub <: B.lb.  In practice there
-are two ways to do that, depicted pictorially here:
-
-```text
-    Before                Option #1            Option #2
-
-             B.ub                B.ub                B.ub
-              /\                 /  \                /  \
-      A.ub   /  \        A.ub   /(B')\       A.ub   /(B')\
-      /   \ /    \           \ /     /           \ /     /
-     /     X      \         __UB____/             UB    /
-    /     / \      \       /  |                   |    /
-   /     /   /      \     /   |                   |   /
-   \     \  /       /    /(A')|                   |  /
-    \      X       /    /     LB            ______LB/
-     \    / \     /    /     / \           / (A')/ \
-      \  /   \   /     \    /   \          \    /   \
-      A.lb    B.lb       A.lb    B.lb        A.lb    B.lb
-```
+Universes form a tree. However, in the compiler, we represent them
+using only a single integer (`ty::UniverseIndex`). This suffices
+because sibling universes never come into contact with one another --
+so while names from U1 and U0 may "interact" (i.e., appear in the same
+constraint etc), if U0 has *another* subuniverse `U1'`, then names
+from `U1` and `U1'` will never interact with one another.
 
-In these diagrams, UB and LB are defined as before.  As you can see,
-the new ranges `A'` and `B'` are quite different from the range that
-would be produced by unifying the variables.
+## Basic unification
 
-### What we do now
+The basic idea of unification is that, when we encounter a type that
+we do not know, we instantiate a type variable `?T` (we will use a
+leading question mark to refer to inference variables of this kind;
+`?'A` will be used for region variables). Later, when we are doing
+type-checking etc, we may encounter a constraint that tells us what
+`?T` has to be.  For example, imagine that `Vec<?T>` is the type of
+some variable `x`, and we encounter some code which states that the
+type of `x` must be equal to `Vec<i32>`. We could write the constraint
+that arises from this as:
 
-Our current technique is to *try* (transactionally) to relate the
-existing bounds of A and B, if there are any (i.e., if `UB(A) != top
-&& LB(B) != bot`).  If that succeeds, we're done.  If it fails, then
-we merge A and B into same variable.
+    Vec<?T> = Vec<i32>
+    
+Since we know that `Vec<T>` is only equal to `Vec<U>` if `T=U`, we can
+further derive from this constraint that `?T = i32`. This general
+process is called **unification**. It's a "tried and true" technique
+and there are good descriptions of it in many places; you can read a
+Rust-oriented description of it in [this blog post][unification1].
 
-This is not clearly the correct course.  For example, if `UB(A) !=
-top` but `LB(B) == bot`, we could conceivably set `LB(B)` to `UB(A)`
-and leave the variables unmerged.  This is sometimes the better
-course, it depends on the program.
+[unification1]: http://smallcultfollowing.com/babysteps/blog/2017/03/25/unification-in-chalk-part-1/
 
-The main case which fails today that I would like to support is:
+## Extending unification with universes
 
-```rust
-fn foo<T>(x: T, y: T) { ... }
+To 
 
-fn bar() {
-    let x: @mut int = @mut 3;
-    let y: @int = @3;
-    foo(x, y);
-}
-```
+## Handling subtyping
 
-In principle, the inferencer ought to find that the parameter `T` to
-`foo(x, y)` is `@const int`.  Today, however, it does not; this is
-because the type variable `T` is merged with the type variable for
-`X`, and thus inherits its UB/LB of `@mut int`.  This leaves no
-flexibility for `T` to later adjust to accommodate `@int`.
-
-Note: `@` and `@mut` are replaced with `Rc<T>` and `Rc<RefCell<T>>` in current Rust.
-
-### What to do when not all bounds are present
-
-In the prior discussion we assumed that A.ub was not top and B.lb was
-not bot.  Unfortunately this is rarely the case.  Often type variables
-have "lopsided" bounds.  For example, if a variable in the program has
-been initialized but has not been used, then its corresponding type
-variable will have a lower bound but no upper bound.  When that
-variable is then used, we would like to know its upper bound---but we
-don't have one!  In this case we'll do different things depending on
-how the variable is being used.
-
-## Transactional support
-
-Whenever we adjust merge variables or adjust their bounds, we always
-keep a record of the old value.  This allows the changes to be undone.
-
-## Regions
-
-I've only talked about type variables here, but region variables
-follow the same principle.  They have upper- and lower-bounds.  A
-region A is a subregion of a region B if A being valid implies that B
-is valid.  This basically corresponds to the block nesting structure:
-the regions for outer block scopes are superregions of those for inner
-block scopes.
-
-## Integral and floating-point type variables
-
-There is a third variety of type variable that we use only for
-inferring the types of unsuffixed integer literals.  Integral type
-variables differ from general-purpose type variables in that there's
-no subtyping relationship among the various integral types, so instead
-of associating each variable with an upper and lower bound, we just
-use simple unification.  Each integer variable is associated with at
-most one integer type.  Floating point types are handled similarly to
-integral types.
-
-## GLB/LUB
-
-Computing the greatest-lower-bound and least-upper-bound of two
-types/regions is generally straightforward except when type variables
-are involved. In that case, we follow a similar "try to use the bounds
-when possible but otherwise merge the variables" strategy.  In other
-words, `GLB(A, B)` where `A` and `B` are variables will often result
-in `A` and `B` being merged and the result being `A`.
-
-## Type coercion
-
-We have a notion of assignability which differs somewhat from
-subtyping; in particular it may cause region borrowing to occur.  See
-the big comment later in this file on Type Coercion for specifics.
-
-### In conclusion
-
-I showed you three ways to relate `A` and `B`.  There are also more,
-of course, though I'm not sure if there are any more sensible options.
-The main point is that there are various options, each of which
-produce a distinct range of types for `A` and `B`.  Depending on what
-the correct values for A and B are, one of these options will be the
-right choice: but of course we don't know the right values for A and B
-yet, that's what we're trying to find!  In our code, we opt to unify
-(Option #1).
-
-# Implementation details
-
-We make use of a trait-like implementation strategy to consolidate
-duplicated code between subtypes, GLB, and LUB computations.  See the
-section on "Type Combining" in combine.rs for more details.
+In truth, outside of trait matching, we rarely encounter *equality* constraints in Rust.
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index c0ca157997bf4..7d37a4a0c7739 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -264,12 +264,20 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
             RelationDir::SupertypeOf => ty::Contravariant,
         };
 
+        let universe = match self.infcx.type_variables.borrow_mut().probe(for_vid) {
+            TypeVariableValue::Unknown { universe } => universe,
+            TypeVariableValue::Known { .. } => {
+                bug!("generalizing `{:?}` for vid `{:?}` which is bound", ty, for_vid)
+            }
+        };
+
         let mut generalize = Generalizer {
             infcx: self.infcx,
             span: self.trace.cause.span,
             for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
             ambient_variance,
             needs_wf: false,
+            universe,
         };
 
         let ty = generalize.relate(&ty, &ty)?;
@@ -284,6 +292,7 @@ struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
     for_vid_sub_root: ty::TyVid,
     ambient_variance: ty::Variance,
     needs_wf: bool, // see the field `needs_wf` in `Generalization`
+    universe: ty::UniverseIndex,
 }
 
 /// Result from a generalization operation. This includes
@@ -465,7 +474,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
 
         // FIXME: This is non-ideal because we don't give a
         // very descriptive origin for this region variable.
-        Ok(self.infcx.next_region_var(MiscVariable(self.span)))
+        Ok(self.infcx.next_region_var(self.universe, MiscVariable(self.span)))
     }
 }
 
diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs
index 72f22856304fa..e84ef58d05034 100644
--- a/src/librustc/infer/fudge.rs
+++ b/src/librustc/infer/fudge.rs
@@ -156,7 +156,11 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> {
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
             ty::ReVar(v) if self.region_vars.contains(&v) => {
-                self.infcx.next_region_var(self.origin.clone())
+                // TODO -- I am not entirely sur how fudging and
+                // universes should work, but using root is a
+                // conservative choice here, and I suspect it doesn't
+                // much matter.
+                self.infcx.next_region_var(ty::UniverseIndex::ROOT, self.origin.clone())
             }
             _ => {
                 r
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index ba6dde3ba6f50..7784579a133ba 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -50,19 +50,24 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         return self.infcx.commit_if_ok(|snapshot| {
             let span = self.trace.cause.span;
 
-            // First, we instantiate each bound region in the subtype with a fresh
-            // region variable.
+            // First, we instantiate each bound region in the supertype with a
+            // fresh concrete region.
+            let (b_prime, skol_map) =
+                self.infcx.skolemize_late_bound_regions(b, snapshot);
+
+            // TODO -- by the end of this patch series, skolemize-late-bound-regions
+            // should be producing new environments with an increased universe.
+            let param_env = self.param_env;
+
+            // Second, we instantiate each bound region in the subtype with a fresh
+            // region variable. These are declared in the innermost universe.
             let (a_prime, _) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
                     span,
+                    param_env.universe,
                     HigherRankedType,
                     a);
 
-            // Second, we instantiate each bound region in the supertype with a
-            // fresh concrete region.
-            let (b_prime, skol_map) =
-                self.infcx.skolemize_late_bound_regions(b, snapshot);
-
             debug!("a_prime={:?}", a_prime);
             debug!("b_prime={:?}", b_prime);
 
@@ -210,10 +215,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             let span = self.trace.cause.span;
             let (a_with_fresh, a_map) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    span, HigherRankedType, a);
+                    span, self.param_env.universe, HigherRankedType, a);
             let (b_with_fresh, _) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    span, HigherRankedType, b);
+                    span, self.param_env.universe, HigherRankedType, b);
 
             // Collect constraints.
             let result0 =
@@ -302,10 +307,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             // Instantiate each bound region with a fresh region variable.
             let (a_with_fresh, a_map) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    self.trace.cause.span, HigherRankedType, a);
+                    self.trace.cause.span, self.param_env.universe, HigherRankedType, a);
             let (b_with_fresh, b_map) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    self.trace.cause.span, HigherRankedType, b);
+                    self.trace.cause.span, self.param_env.universe, HigherRankedType, b);
             let a_vars = var_ids(self, &a_map);
             let b_vars = var_ids(self, &b_map);
 
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index f411ad5b29602..9083164bdf021 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -975,18 +975,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             .new_key(None)
     }
 
-    pub fn next_region_var(&self, origin: RegionVariableOrigin)
+    pub fn next_region_var(&self,
+                           universe: ty::UniverseIndex,
+                           origin: RegionVariableOrigin)
                            -> ty::Region<'tcx> {
-        self.tcx.mk_region(ty::ReVar(self.region_vars.new_region_var(origin)))
+        self.tcx.mk_region(ty::ReVar(self.region_vars.new_region_var(universe, origin)))
     }
 
     /// Create a region inference variable for the given
     /// region parameter definition.
     pub fn region_var_for_def(&self,
+                              universe: ty::UniverseIndex,
                               span: Span,
                               def: &ty::RegionParameterDef)
                               -> ty::Region<'tcx> {
-        self.next_region_var(EarlyBoundRegion(span, def.name))
+        self.next_region_var(universe, EarlyBoundRegion(span, def.name))
     }
 
     /// Create a type inference variable for the given
@@ -1019,7 +1022,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                  def_id: DefId)
                                  -> &'tcx Substs<'tcx> {
         Substs::for_item(self.tcx, def_id, |def, _| {
-            self.region_var_for_def(span, def)
+            self.region_var_for_def(universe, span, def)
         }, |def, _| {
             self.type_var_for_def(universe, span, def)
         })
@@ -1222,6 +1225,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn replace_late_bound_regions_with_fresh_var<T>(
         &self,
         span: Span,
+        universe: ty::UniverseIndex,
         lbrct: LateBoundRegionConversionTime,
         value: &ty::Binder<T>)
         -> (T, FxHashMap<ty::BoundRegion, ty::Region<'tcx>>)
@@ -1229,7 +1233,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     {
         self.tcx.replace_late_bound_regions(
             value,
-            |br| self.next_region_var(LateBoundRegion(span, br, lbrct)))
+            |br| self.next_region_var(universe, LateBoundRegion(span, br, lbrct)))
     }
 
     /// Given a higher-ranked projection predicate like:
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index acc4d13671904..b71a0ac6cb759 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -29,6 +29,7 @@ use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased};
 use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
 
 use std::cell::{Cell, RefCell};
+use std::cmp;
 use std::fmt;
 use std::mem;
 use std::u32;
@@ -181,7 +182,8 @@ pub type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
 
 #[derive(Clone, Debug)]
 struct RegionVariableInfo {
-    origin: RegionVariableOrigin
+    origin: RegionVariableOrigin,
+    universe: ty::UniverseIndex,
 }
 
 pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
@@ -461,10 +463,14 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         len as u32
     }
 
-    pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid {
+    pub fn new_region_var(&self,
+                          universe: ty::UniverseIndex,
+                          origin: RegionVariableOrigin)
+                          -> RegionVid {
         let vid = RegionVid { index: self.num_vars() };
         self.var_infos.borrow_mut().push(RegionVariableInfo {
-            origin: origin.clone()
+            origin: origin.clone(),
+            universe,
         });
 
         let u_vid = self.unification_table.borrow_mut().new_key(
@@ -480,6 +486,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         return vid;
     }
 
+    pub fn var_universe(&self, vid: RegionVid) -> ty::UniverseIndex {
+        self.var_infos.borrow()[vid.index as usize].universe
+    }
+
     pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin {
         self.var_infos.borrow()[vid.index as usize].origin.clone()
     }
@@ -839,7 +849,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         if let Some(&c) = self.combine_map(t).borrow().get(&vars) {
             return self.tcx.mk_region(ReVar(c));
         }
-        let c = self.new_region_var(MiscVariable(origin.span()));
+        let a_universe = self.universe(a);
+        let b_universe = self.universe(b);
+        let c_universe = cmp::max(a_universe, b_universe);
+        let c = self.new_region_var(c_universe, MiscVariable(origin.span()));
         self.combine_map(t).borrow_mut().insert(vars, c);
         if self.in_snapshot() {
             self.undo_log.borrow_mut().push(AddCombination(t, vars));
@@ -850,6 +863,27 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         self.tcx.mk_region(ReVar(c))
     }
 
+    fn universe(&self, region: Region<'tcx>) -> ty::UniverseIndex {
+        match *region {
+            ty::ReScope(..) |
+            ty::ReStatic |
+            ty::ReEmpty |
+            ty::ReErased |
+            ty::ReFree(..) |
+            ty::ReEarlyBound(..) =>
+                ty::UniverseIndex::ROOT,
+
+            ty::ReSkolemized(u, _) =>
+                u,
+
+            ty::ReVar(vid) =>
+                self.var_infos.borrow()[vid.index as usize].universe,
+
+            ty::ReLateBound(..) =>
+                bug!("universe(): encountered bound region {:?}", region),
+        }
+    }
+
     pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) -> Vec<RegionVid> {
         self.undo_log.borrow()[mark.length..]
             .iter()
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index e4e53a6692751..4458f30394d5e 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -199,6 +199,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 let mut selcx = SelectionContext::new(self);
                 let (data, _) = self.replace_late_bound_regions_with_fresh_var(
                     obligation.cause.span,
+                    obligation.param_env.universe,
                     infer::LateBoundRegionConversionTime::HigherRankedType,
                     data);
                 let normalized = super::normalize_projection_type(
diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs
index a3ff7a041ca07..a77f986187ff7 100644
--- a/src/librustc_mir/transform/nll/renumber.rs
+++ b/src/librustc_mir/transform/nll/renumber.rs
@@ -30,7 +30,8 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
     // declared on the function signature.
     let free_region_inference_vars = (0..free_regions.indices.len())
         .map(|_| {
-            infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
+            infcx.next_region_var(ty::UniverseIndex::ROOT,
+                                  rustc_infer::MiscVariable(DUMMY_SP))
         })
         .collect();
 
@@ -68,7 +69,8 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
             .fold_regions(value, &mut false, |_region, _depth| {
                 self.num_region_variables += 1;
                 self.infcx
-                    .next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
+                    .next_region_var(ty::UniverseIndex::ROOT,
+                                     rustc_infer::MiscVariable(DUMMY_SP))
             })
     }
 
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index 8d6458d793474..4582558239131 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -37,10 +37,10 @@ impl MirPass for SanityCheck {
         let id = src.item_id();
         let def_id = tcx.hir.local_def_id(id);
         if !tcx.has_attr(def_id, "rustc_mir_borrowck") {
-            debug!("skipping rustc_peek::SanityCheck on {}", tcx.item_path_str(def_id));
+            debug!("skipping rustc_peek::SanityCheck on {:?}", def_id);
             return;
         } else {
-            debug!("running rustc_peek::SanityCheck on {}", tcx.item_path_str(def_id));
+            debug!("running rustc_peek::SanityCheck on {:?}", def_id);
         }
 
         let attributes = tcx.get_attrs(def_id);
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 2fbdc3c236ede..9f60243f79440 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -232,7 +232,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         //    ref x | ref const x | ref mut x
                         // then `x` is assigned a value of type `&M T` where M is the mutability
                         // and T is the expected type.
-                        let region_var = self.next_region_var(infer::PatternRegion(pat.span));
+                        let region_var = self.next_region_var(self.param_env.universe,
+                                                              infer::PatternRegion(pat.span));
                         let mt = ty::TypeAndMut { ty: expected, mutbl: mutbl };
                         let region_ty = tcx.mk_ref(region_var, mt);
 
@@ -333,7 +334,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                 ty::UniverseIndex::ROOT,
                                 TypeVariableOrigin::TypeInference(inner.span));
                             let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl };
-                            let region = self.next_region_var(infer::PatternRegion(pat.span));
+                            let region = self.next_region_var(self.param_env.universe,
+                                                              infer::PatternRegion(pat.span));
                             let rptr_ty = tcx.mk_ref(region, mt);
                             debug!("check_pat_walk: demanding {:?} = {:?}", expected, rptr_ty);
                             let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty);
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 91ce4511a31cb..8330d24ed24d3 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -110,10 +110,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // fnmut vs fnonce. If so, we have to defer further processing.
                 if self.closure_kind(def_id).is_none() {
                     let closure_ty = self.fn_sig(def_id).subst(self.tcx, substs.substs);
-                    let fn_sig = self.replace_late_bound_regions_with_fresh_var(call_expr.span,
-                                                                   infer::FnCall,
-                                                                   &closure_ty)
-                        .0;
+                    let fn_sig = self.replace_late_bound_regions_with_fresh_var(
+                        call_expr.span,
+                        self.param_env.universe,
+                        infer::FnCall,
+                        &closure_ty).0;
                     let adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference);
                     self.record_deferred_call_resolution(def_id, DeferredCallResolution {
                         call_expr,
@@ -260,7 +261,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // previously appeared within a `Binder<>` and hence would not
         // have been normalized before.
         let fn_sig =
-            self.replace_late_bound_regions_with_fresh_var(call_expr.span, infer::FnCall, &fn_sig)
+            self.replace_late_bound_regions_with_fresh_var(call_expr.span,
+                                                           self.param_env.universe,
+                                                           infer::FnCall,
+                                                           &fn_sig)
                 .0;
         let fn_sig = self.normalize_associated_types_in(call_expr.span, &fn_sig);
 
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index f7a086c160b64..fa5a7c25c0c22 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -492,6 +492,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // Instantiate (this part of..) S to S', i.e., with fresh variables.
                 let (supplied_ty, _) = self.infcx.replace_late_bound_regions_with_fresh_var(
                     hir_ty.span,
+                    ty::UniverseIndex::ROOT,
                     LateBoundRegionConversionTime::FnCall,
                     &ty::Binder(supplied_ty),
                 ); // recreated from (*) above
@@ -508,6 +509,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
             let (supplied_output_ty, _) = self.infcx.replace_late_bound_regions_with_fresh_var(
                 decl.output.span(),
+                ty::UniverseIndex::ROOT,
                 LateBoundRegionConversionTime::FnCall,
                 &supplied_sig.output(),
             );
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 3941b3c8f1b5a..94b41fb9ec694 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -135,6 +135,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
     }
 
     fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
+        debug!("Coerce.unify({:?}, {:?})", a, b);
         self.commit_if_ok(|_| {
             if self.use_lub {
                 self.at(&self.cause, self.fcx.param_env)
@@ -359,7 +360,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                 if r_borrow_var.is_none() {
                     // create var lazilly, at most once
                     let coercion = Coercion(span);
-                    let r = self.next_region_var(coercion);
+                    let r = self.next_region_var(self.param_env.universe, coercion);
                     r_borrow_var = Some(r); // [4] above
                 }
                 r_borrow_var.unwrap()
@@ -463,7 +464,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                 coerce_mutbls(mt_a.mutbl, mt_b.mutbl)?;
 
                 let coercion = Coercion(self.cause.span);
-                let r_borrow = self.next_region_var(coercion);
+                let r_borrow = self.next_region_var(self.param_env.universe, coercion);
                 Some((Adjustment {
                     kind: Adjust::Deref(None),
                     target: mt_a.ty
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index d6c0156cbfec2..951bb4c747e16 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -230,9 +230,11 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         let mut selcx = traits::SelectionContext::new(&infcx);
 
         let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_skol_substs);
-        let (impl_m_own_bounds, _) = infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
-                                                       infer::HigherRankedType,
-                                                       &ty::Binder(impl_m_own_bounds.predicates));
+        let (impl_m_own_bounds, _) = infcx.replace_late_bound_regions_with_fresh_var(
+            impl_m_span,
+            param_env.universe,
+            infer::HigherRankedType,
+            &ty::Binder(impl_m_own_bounds.predicates));
         for predicate in impl_m_own_bounds {
             let traits::Normalized { value: predicate, obligations } =
                 traits::normalize(&mut selcx, param_env, normalize_cause.clone(), &predicate);
@@ -258,9 +260,11 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         let tcx = infcx.tcx;
 
         let (impl_sig, _) =
-            infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
-                                                            infer::HigherRankedType,
-                                                            &tcx.fn_sig(impl_m.def_id));
+            infcx.replace_late_bound_regions_with_fresh_var(
+                impl_m_span,
+                param_env.universe,
+                infer::HigherRankedType,
+                &tcx.fn_sig(impl_m.def_id));
         let impl_sig =
             inh.normalize_associated_types_in(impl_m_span,
                                               impl_m_node_id,
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index b6b39a807563a..99b10efa01425 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -149,7 +149,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
         let mut target = autoderef.unambiguous_final_ty();
 
         if let Some(mutbl) = pick.autoref {
-            let region = self.next_region_var(infer::Autoref(self.span));
+            let region = self.next_region_var(self.param_env.universe, infer::Autoref(self.span));
             target = self.tcx.mk_ref(region, ty::TypeAndMut {
                 mutbl,
                 ty: target
@@ -303,7 +303,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
                     = provided.as_ref().and_then(|p| p.lifetimes.get(i - parent_substs.len())) {
                 AstConv::ast_region_to_region(self.fcx, lifetime, Some(def))
             } else {
-                self.region_var_for_def(self.span, def)
+                self.region_var_for_def(self.param_env.universe, self.span, def)
             }
         }, |def, _cur_substs| {
             let i = def.index as usize;
@@ -597,7 +597,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
         where T: TypeFoldable<'tcx>
     {
         self.fcx
-            .replace_late_bound_regions_with_fresh_var(self.span, infer::FnCall, value)
+            .replace_late_bound_regions_with_fresh_var(self.span,
+                                                       self.param_env.universe,
+                                                       infer::FnCall,
+                                                       value)
             .0
     }
 }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index af86570309dfd..9a0aec595604d 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -248,7 +248,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // Construct a trait-reference `self_ty : Trait<input_tys>`
         let substs = Substs::for_item(self.tcx,
                                       trait_def_id,
-                                      |def, _| self.region_var_for_def(span, def),
+                                      |def, _| self.region_var_for_def(self.param_env.universe,
+                                                                       span,
+                                                                       def),
                                       |def, _substs| {
             if def.index == 0 {
                 self_ty
@@ -296,6 +298,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // may reference those regions.
         let fn_sig = tcx.fn_sig(def_id);
         let fn_sig = self.replace_late_bound_regions_with_fresh_var(span,
+                                                                    self.param_env.universe,
                                                                     infer::FnCall,
                                                                     &fn_sig).0;
         let fn_sig = fn_sig.subst(self.tcx, substs);
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 646867283e23b..3cb58beaa2233 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -698,7 +698,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                                                             method.def_id);
                     let fty = fty.subst(self.tcx, substs);
                     let (fty, _) = self.replace_late_bound_regions_with_fresh_var(
-                        self.span, infer::FnCall, &fty);
+                        self.span, self.param_env.universe, infer::FnCall, &fty);
 
                     if let Some(self_ty) = self_ty {
                         if let Err(_) = self.at(&ObligationCause::dummy(), self.param_env)
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index a1f2a0f3a5461..e65d3f7a9bd7a 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1648,7 +1648,7 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> {
             Some(def) => infer::EarlyBoundRegion(span, def.name),
             None => infer::MiscVariable(span)
         };
-        Some(self.next_region_var(v))
+        Some(self.next_region_var(self.param_env.universe, v))
     }
 
     fn ty_infer(&self, span: Span) -> Ty<'tcx> {
@@ -1671,6 +1671,7 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> {
         let (trait_ref, _) =
             self.replace_late_bound_regions_with_fresh_var(
                 span,
+                self.param_env.universe,
                 infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
                 &poly_trait_ref);
 
@@ -3671,7 +3672,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // Finally, borrowck is charged with guaranteeing that the
                 // value whose address was taken can actually be made to live
                 // as long as it needs to live.
-                let region = self.next_region_var(infer::AddrOfRegion(expr.span));
+                let region = self.next_region_var(self.param_env.universe,
+                                                  infer::AddrOfRegion(expr.span));
                 tcx.mk_ref(region, tm)
             }
           }
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index ad7978480a6b1..f2d9e77975c99 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -1809,6 +1809,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
                     let (outlives, _) =
                         self.replace_late_bound_regions_with_fresh_var(
                             span,
+                            self.param_env.universe,
                             infer::AssocTypeProjection(projection_ty.item_def_id),
                             &outlives);
 
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index d179b390a2918..4b07219603bf6 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -138,7 +138,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     }
                     hir::CaptureByRef => {
                         let origin = UpvarRegion(upvar_id, span);
-                        let freevar_region = self.next_region_var(origin);
+                        let freevar_region = self.next_region_var(self.param_env.universe, origin);
                         let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow,
                                                              region: freevar_region };
                         ty::UpvarCapture::ByRef(upvar_borrow)

From 57c8cba6a529668900d8f2aa46f18ae49040eb20 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 17 Aug 2017 10:43:07 -0400
Subject: [PATCH 19/63] make solving "universe aware", in a simplistic way

Since we don't yet have `if (C) R` region constraints, the only way
for `'a` to outlive `'b` if `'b` is in a different universe that is
not nameable from `'a` is for `'a` to be `'static`.
---
 src/librustc/infer/region_inference/mod.rs | 32 +++++++++++++++++++++-
 src/librustc/ty/mod.rs                     |  6 ++++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index b71a0ac6cb759..1fa114f9e751c 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -1158,13 +1158,43 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             _ => {}
         }
 
+        let b_universe = self.var_infos.borrow()[b_vid.index as usize].universe;
+
         match *b_data {
             Value(cur_region) => {
-                let lub = self.lub_concrete_regions(region_rels, a_region, cur_region);
+                let mut lub = self.lub_concrete_regions(region_rels, a_region, cur_region);
                 if lub == cur_region {
                     return false;
                 }
 
+                // Find the universe of the new value (`lub`) and
+                // check whether this value is something that we can
+                // legally name in this variable. If not, promote the
+                // variable to `'static`, which is surely greater than
+                // or equal to `lub`. This is obviously a kind of sub-optimal
+                // choice -- in the future, when we incorporate a knowledge
+                // of the parameter environment, we might be able to find a
+                // tighter bound than `'static`.
+                //
+                // To make this more concrete, imagine a bound like:
+                //
+                //     for<'a> '0: 'a
+                //
+                // Here we have that `'0` must outlive `'a` -- no
+                // matter what `'a` is. When solving such a
+                // constraint, we would initially assign `'0` to be
+                // `'empty`. We would then compute the LUB of `'empty`
+                // and `'a` (which is something like `ReSkolemized(1)`),
+                // resulting in `'a`.
+                //
+                // At this point, `lub_universe` would be `1` and
+                // `b_universe` would be `0`, and hence we would wind
+                // up promoting `lub` to `'static`.
+                let lub_universe = self.universe(lub);
+                if !lub_universe.is_visible_in(b_universe) {
+                    lub = self.tcx.types.re_static;
+                }
+
                 debug!("Expanding value of {:?} from {:?} to {:?}",
                        b_vid,
                        cur_region,
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index af2bb1b1e7fab..d6563651b075d 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1302,6 +1302,12 @@ impl UniverseIndex {
         UniverseIndex(v)
     }
 
+    /// Indicates whether a name in this universe is visible in the
+    /// universe `other`.
+    pub fn is_visible_in(self, other: UniverseIndex) -> bool {
+        self <= other
+    }
+
     pub fn as_u32(&self) -> u32 {
         self.0
     }

From b2f4105cf1ee45691228963ce1868d3da2687326 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 20 Aug 2017 23:06:36 -0700
Subject: [PATCH 20/63] move param-env from CombineFields into the individual
 operations

---
 src/librustc/infer/at.rs                | 16 +++++-----
 src/librustc/infer/combine.rs           | 38 +++++++++++++++---------
 src/librustc/infer/equate.rs            | 23 +++++++++++----
 src/librustc/infer/glb.rs               | 17 ++++++-----
 src/librustc/infer/higher_ranked/mod.rs | 39 +++++++++++++++----------
 src/librustc/infer/lub.rs               | 17 ++++++-----
 src/librustc/infer/mod.rs               |  7 ++---
 src/librustc/infer/sub.rs               | 28 ++++++++++++------
 8 files changed, 116 insertions(+), 69 deletions(-)

diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs
index 3fd7ee276729f..44951f0f915fb 100644
--- a/src/librustc/infer/at.rs
+++ b/src/librustc/infer/at.rs
@@ -209,8 +209,8 @@ impl<'a, 'gcx, 'tcx> Trace<'a, 'gcx, '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)
+            let mut fields = at.infcx.combine_fields(trace);
+            fields.sub(at.param_env, a_is_expected)
                   .relate(a, b)
                   .map(move |_| InferOk { value: (), obligations: fields.obligations })
         })
@@ -227,8 +227,8 @@ impl<'a, 'gcx, 'tcx> Trace<'a, 'gcx, '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)
+            let mut fields = at.infcx.combine_fields(trace);
+            fields.equate(at.param_env, a_is_expected)
                   .relate(a, b)
                   .map(move |_| InferOk { value: (), obligations: fields.obligations })
         })
@@ -243,8 +243,8 @@ impl<'a, 'gcx, 'tcx> Trace<'a, 'gcx, '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)
+            let mut fields = at.infcx.combine_fields(trace);
+            fields.lub(at.param_env, a_is_expected)
                   .relate(a, b)
                   .map(move |t| InferOk { value: t, obligations: fields.obligations })
         })
@@ -259,8 +259,8 @@ impl<'a, 'gcx, 'tcx> Trace<'a, 'gcx, '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)
+            let mut fields = at.infcx.combine_fields(trace);
+            fields.glb(at.param_env, a_is_expected)
                   .relate(a, b)
                   .map(move |t| InferOk { value: t, obligations: fields.obligations })
         })
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 7d37a4a0c7739..412ca286188c3 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -55,7 +55,6 @@ 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>,
 }
 
@@ -159,20 +158,32 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
         self.infcx.tcx
     }
 
-    pub fn equate<'a>(&'a mut self, a_is_expected: bool) -> Equate<'a, 'infcx, 'gcx, 'tcx> {
-        Equate::new(self, a_is_expected)
+    pub fn equate<'a>(&'a mut self,
+                      param_env: ty::ParamEnv<'tcx>,
+                      a_is_expected: bool)
+                      -> Equate<'a, 'infcx, 'gcx, 'tcx> {
+        Equate::new(self, param_env, a_is_expected)
     }
 
-    pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'gcx, 'tcx> {
-        Sub::new(self, a_is_expected)
+    pub fn sub<'a>(&'a mut self,
+                   param_env: ty::ParamEnv<'tcx>,
+                   a_is_expected: bool)
+                   -> Sub<'a, 'infcx, 'gcx, 'tcx> {
+        Sub::new(self, param_env, a_is_expected)
     }
 
-    pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'gcx, 'tcx> {
-        Lub::new(self, a_is_expected)
+    pub fn lub<'a>(&'a mut self,
+                   param_env: ty::ParamEnv<'tcx>,
+                   a_is_expected: bool)
+                   -> Lub<'a, 'infcx, 'gcx, 'tcx> {
+        Lub::new(self, param_env, a_is_expected)
     }
 
-    pub fn glb<'a>(&'a mut self, a_is_expected: bool) -> Glb<'a, 'infcx, 'gcx, 'tcx> {
-        Glb::new(self, a_is_expected)
+    pub fn glb<'a>(&'a mut self,
+                   param_env: ty::ParamEnv<'tcx>,
+                   a_is_expected: bool)
+                   -> Glb<'a, 'infcx, 'gcx, 'tcx> {
+        Glb::new(self, param_env, a_is_expected)
     }
 
     /// Here dir is either EqTo, SubtypeOf, or SupertypeOf. The
@@ -185,6 +196,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
     /// of `a_ty`. Generalization introduces other inference
     /// variables wherever subtyping could occur.
     pub fn instantiate(&mut self,
+                       param_env: ty::ParamEnv<'tcx>,
                        a_ty: Ty<'tcx>,
                        dir: RelationDir,
                        b_vid: ty::TyVid,
@@ -216,7 +228,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
 
         if needs_wf {
             self.obligations.push(Obligation::new(self.trace.cause.clone(),
-                                                  self.param_env,
+                                                  param_env,
                                                   ty::Predicate::WellFormed(b_ty)));
         }
 
@@ -227,9 +239,9 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
         // to associate causes/spans with each of the relations in
         // the stack to get this right.
         match dir {
-            EqTo => self.equate(a_is_expected).relate(&a_ty, &b_ty),
-            SubtypeOf => self.sub(a_is_expected).relate(&a_ty, &b_ty),
-            SupertypeOf => self.sub(a_is_expected).relate_with_variance(
+            EqTo => self.equate(param_env, a_is_expected).relate(&a_ty, &b_ty),
+            SubtypeOf => self.sub(param_env, a_is_expected).relate(&a_ty, &b_ty),
+            SupertypeOf => self.sub(param_env, a_is_expected).relate_with_variance(
                 ty::Contravariant, &a_ty, &b_ty),
         }?;
 
diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs
index f9ffaee81f157..b2e3318b65761 100644
--- a/src/librustc/infer/equate.rs
+++ b/src/librustc/infer/equate.rs
@@ -22,13 +22,16 @@ use ty::relate::{self, Relate, RelateResult, TypeRelation};
 pub struct Equate<'combine, 'infcx: 'combine, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> {
     fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>,
     a_is_expected: bool,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 impl<'combine, 'infcx, 'gcx, 'tcx> Equate<'combine, 'infcx, 'gcx, 'tcx> {
-    pub fn new(fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>, a_is_expected: bool)
+    pub fn new(fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>,
+               param_env: ty::ParamEnv<'tcx>,
+               a_is_expected: bool)
         -> Equate<'combine, 'infcx, 'gcx, 'tcx>
     {
-        Equate { fields: fields, a_is_expected: a_is_expected }
+        Equate { fields, a_is_expected, param_env }
     }
 }
 
@@ -81,12 +84,20 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
             }
 
             (&ty::TyInfer(TyVar(a_id)), _) => {
-                self.fields.instantiate(b, RelationDir::EqTo, a_id, self.a_is_expected)?;
+                self.fields.instantiate(self.param_env,
+                                        b,
+                                        RelationDir::EqTo,
+                                        a_id,
+                                        self.a_is_expected)?;
                 Ok(a)
             }
 
             (_, &ty::TyInfer(TyVar(b_id))) => {
-                self.fields.instantiate(a, RelationDir::EqTo, b_id, self.a_is_expected)?;
+                self.fields.instantiate(self.param_env,
+                                        a,
+                                        RelationDir::EqTo,
+                                        b_id,
+                                        self.a_is_expected)?;
                 Ok(a)
             }
 
@@ -112,7 +123,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                   -> RelateResult<'tcx, ty::Binder<T>>
         where T: Relate<'tcx>
     {
-        self.fields.higher_ranked_sub(a, b, self.a_is_expected)?;
-        self.fields.higher_ranked_sub(b, a, self.a_is_expected)
+        self.fields.higher_ranked_sub(self.param_env, a, b, self.a_is_expected)?;
+        self.fields.higher_ranked_sub(self.param_env, b, a, self.a_is_expected)
     }
 }
diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs
index d7afeba7dc96b..f8c364a730714 100644
--- a/src/librustc/infer/glb.rs
+++ b/src/librustc/infer/glb.rs
@@ -21,13 +21,16 @@ use ty::relate::{Relate, RelateResult, TypeRelation};
 pub struct Glb<'combine, 'infcx: 'combine, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> {
     fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>,
     a_is_expected: bool,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 impl<'combine, 'infcx, 'gcx, 'tcx> Glb<'combine, 'infcx, 'gcx, 'tcx> {
-    pub fn new(fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>, a_is_expected: bool)
-        -> Glb<'combine, 'infcx, 'gcx, 'tcx>
+    pub fn new(fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>,
+               param_env: ty::ParamEnv<'tcx>,
+               a_is_expected: bool)
+               -> Glb<'combine, 'infcx, 'gcx, 'tcx>
     {
-        Glb { fields: fields, a_is_expected: a_is_expected }
+        Glb { fields, a_is_expected, param_env }
     }
 }
 
@@ -47,11 +50,11 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                                              -> RelateResult<'tcx, T>
     {
         match variance {
-            ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
+            ty::Invariant => self.fields.equate(self.param_env, self.a_is_expected).relate(a, b),
             ty::Covariant => self.relate(a, b),
             // FIXME(#41044) -- not correct, need test
             ty::Bivariant => Ok(a.clone()),
-            ty::Contravariant => self.fields.lub(self.a_is_expected).relate(a, b),
+            ty::Contravariant => self.fields.lub(self.param_env, self.a_is_expected).relate(a, b),
         }
     }
 
@@ -74,7 +77,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                   -> RelateResult<'tcx, ty::Binder<T>>
         where T: Relate<'tcx>
     {
-        self.fields.higher_ranked_glb(a, b, self.a_is_expected)
+        self.fields.higher_ranked_glb(self.param_env, a, b, self.a_is_expected)
     }
 }
 
@@ -90,7 +93,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> LatticeDir<'infcx, 'gcx, 'tcx>
     }
 
     fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> {
-        let mut sub = self.fields.sub(self.a_is_expected);
+        let mut sub = self.fields.sub(self.param_env, self.a_is_expected);
         sub.relate(&v, &a)?;
         sub.relate(&v, &b)?;
         Ok(())
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index 7784579a133ba..d3c9f55ceffb7 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -30,7 +30,11 @@ pub struct HrMatchResult<U> {
 }
 
 impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
-    pub fn higher_ranked_sub<T>(&mut self, a: &Binder<T>, b: &Binder<T>, a_is_expected: bool)
+    pub fn higher_ranked_sub<T>(&mut self,
+                                param_env: ty::ParamEnv<'tcx>,
+                                a: &Binder<T>,
+                                b: &Binder<T>,
+                                a_is_expected: bool)
                                 -> RelateResult<'tcx, Binder<T>>
         where T: Relate<'tcx>
     {
@@ -55,10 +59,6 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             let (b_prime, skol_map) =
                 self.infcx.skolemize_late_bound_regions(b, snapshot);
 
-            // TODO -- by the end of this patch series, skolemize-late-bound-regions
-            // should be producing new environments with an increased universe.
-            let param_env = self.param_env;
-
             // Second, we instantiate each bound region in the subtype with a fresh
             // region variable. These are declared in the innermost universe.
             let (a_prime, _) =
@@ -72,7 +72,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             debug!("b_prime={:?}", b_prime);
 
             // Compare types now that bound regions have been replaced.
-            let result = self.sub(a_is_expected).relate(&a_prime, &b_prime)?;
+            let result = self.sub(param_env, a_is_expected).relate(&a_prime, &b_prime)?;
 
             // Presuming type comparison succeeds, we need to check
             // that the skolemized regions do not "leak".
@@ -102,6 +102,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
     /// that do not appear in `T`. If that happens, those regions are
     /// unconstrained, and this routine replaces them with `'static`.
     pub fn higher_ranked_match<T, U>(&mut self,
+                                     param_env: ty::ParamEnv<'tcx>,
                                      a_pair: &Binder<(T, U)>,
                                      b_match: &T,
                                      a_is_expected: bool)
@@ -124,7 +125,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             debug!("higher_ranked_match: skol_map={:?}", skol_map);
 
             // Equate types now that bound regions have been replaced.
-            self.equate(a_is_expected).relate(&a_match, &b_match)?;
+            self.equate(param_env, a_is_expected).relate(&a_match, &b_match)?;
 
             // Map each skolemized region to a vector of other regions that it
             // must be equated with. (Note that this vector may include other
@@ -204,7 +205,11 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         });
     }
 
-    pub fn higher_ranked_lub<T>(&mut self, a: &Binder<T>, b: &Binder<T>, a_is_expected: bool)
+    pub fn higher_ranked_lub<T>(&mut self,
+                                param_env: ty::ParamEnv<'tcx>,
+                                a: &Binder<T>,
+                                b: &Binder<T>,
+                                a_is_expected: bool)
                                 -> RelateResult<'tcx, Binder<T>>
         where T: Relate<'tcx>
     {
@@ -215,14 +220,14 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             let span = self.trace.cause.span;
             let (a_with_fresh, a_map) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    span, self.param_env.universe, HigherRankedType, a);
+                    span, param_env.universe, HigherRankedType, a);
             let (b_with_fresh, _) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    span, self.param_env.universe, HigherRankedType, b);
+                    span, param_env.universe, HigherRankedType, b);
 
             // Collect constraints.
             let result0 =
-                self.lub(a_is_expected).relate(&a_with_fresh, &b_with_fresh)?;
+                self.lub(param_env, a_is_expected).relate(&a_with_fresh, &b_with_fresh)?;
             let result0 =
                 self.infcx.resolve_type_vars_if_possible(&result0);
             debug!("lub result0 = {:?}", result0);
@@ -294,7 +299,11 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         }
     }
 
-    pub fn higher_ranked_glb<T>(&mut self, a: &Binder<T>, b: &Binder<T>, a_is_expected: bool)
+    pub fn higher_ranked_glb<T>(&mut self,
+                                param_env: ty::ParamEnv<'tcx>,
+                                a: &Binder<T>,
+                                b: &Binder<T>,
+                                a_is_expected: bool)
                                 -> RelateResult<'tcx, Binder<T>>
         where T: Relate<'tcx>
     {
@@ -307,16 +316,16 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             // Instantiate each bound region with a fresh region variable.
             let (a_with_fresh, a_map) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    self.trace.cause.span, self.param_env.universe, HigherRankedType, a);
+                    self.trace.cause.span, param_env.universe, HigherRankedType, a);
             let (b_with_fresh, b_map) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    self.trace.cause.span, self.param_env.universe, HigherRankedType, b);
+                    self.trace.cause.span, param_env.universe, HigherRankedType, b);
             let a_vars = var_ids(self, &a_map);
             let b_vars = var_ids(self, &b_map);
 
             // Collect constraints.
             let result0 =
-                self.glb(a_is_expected).relate(&a_with_fresh, &b_with_fresh)?;
+                self.glb(param_env, a_is_expected).relate(&a_with_fresh, &b_with_fresh)?;
             let result0 =
                 self.infcx.resolve_type_vars_if_possible(&result0);
             debug!("glb result0 = {:?}", result0);
diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs
index 04b470b29fc5e..b1a75f48007f0 100644
--- a/src/librustc/infer/lub.rs
+++ b/src/librustc/infer/lub.rs
@@ -21,13 +21,16 @@ use ty::relate::{Relate, RelateResult, TypeRelation};
 pub struct Lub<'combine, 'infcx: 'combine, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> {
     fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>,
     a_is_expected: bool,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 impl<'combine, 'infcx, 'gcx, 'tcx> Lub<'combine, 'infcx, 'gcx, 'tcx> {
-    pub fn new(fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>, a_is_expected: bool)
-        -> Lub<'combine, 'infcx, 'gcx, 'tcx>
+    pub fn new(fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>,
+               param_env: ty::ParamEnv<'tcx>,
+               a_is_expected: bool)
+               -> Lub<'combine, 'infcx, 'gcx, 'tcx>
     {
-        Lub { fields: fields, a_is_expected: a_is_expected }
+        Lub { fields, a_is_expected, param_env }
     }
 }
 
@@ -47,11 +50,11 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                                              -> RelateResult<'tcx, T>
     {
         match variance {
-            ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
+            ty::Invariant => self.fields.equate(self.param_env, self.a_is_expected).relate(a, b),
             ty::Covariant => self.relate(a, b),
             // FIXME(#41044) -- not correct, need test
             ty::Bivariant => Ok(a.clone()),
-            ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b),
+            ty::Contravariant => self.fields.glb(self.param_env, self.a_is_expected).relate(a, b),
         }
     }
 
@@ -74,7 +77,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                   -> RelateResult<'tcx, ty::Binder<T>>
         where T: Relate<'tcx>
     {
-        self.fields.higher_ranked_lub(a, b, self.a_is_expected)
+        self.fields.higher_ranked_lub(self.param_env, a, b, self.a_is_expected)
     }
 }
 
@@ -90,7 +93,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> LatticeDir<'infcx, 'gcx, 'tcx>
     }
 
     fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> {
-        let mut sub = self.fields.sub(self.a_is_expected);
+        let mut sub = self.fields.sub(self.param_env, self.a_is_expected);
         sub.relate(&a, &v)?;
         sub.relate(&b, &v)?;
         Ok(())
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 9083164bdf021..3a8333e1163de 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -661,13 +661,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         return variables;
     }
 
-    fn combine_fields(&'a self, trace: TypeTrace<'tcx>, param_env: ty::ParamEnv<'tcx>)
+    fn combine_fields(&'a self, trace: TypeTrace<'tcx>)
                       -> CombineFields<'a, 'gcx, 'tcx> {
         CombineFields {
             infcx: self,
             trace,
             cause: None,
-            param_env,
             obligations: PredicateObligations::new(),
         }
     }
@@ -1264,8 +1263,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             values: TraitRefs(ExpectedFound::new(true, match_pair.skip_binder().0, match_b))
         };
 
-        let mut combine = self.combine_fields(trace, param_env);
-        let result = combine.higher_ranked_match(&match_pair, &match_b, true)?;
+        let mut combine = self.combine_fields(trace);
+        let result = combine.higher_ranked_match(param_env, &match_pair, &match_b, true)?;
         Ok(InferOk { value: result, obligations: combine.obligations })
     }
 
diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs
index 4056999681352..bf36af86464ac 100644
--- a/src/librustc/infer/sub.rs
+++ b/src/librustc/infer/sub.rs
@@ -22,13 +22,16 @@ use std::mem;
 pub struct Sub<'combine, 'infcx: 'combine, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> {
     fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>,
     a_is_expected: bool,
+    param_env: ty::ParamEnv<'tcx>,
 }
 
 impl<'combine, 'infcx, 'gcx, 'tcx> Sub<'combine, 'infcx, 'gcx, 'tcx> {
-    pub fn new(f: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>, a_is_expected: bool)
-        -> Sub<'combine, 'infcx, 'gcx, 'tcx>
+    pub fn new(fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>,
+               param_env: ty::ParamEnv<'tcx>,
+               a_is_expected: bool)
+               -> Sub<'combine, 'infcx, 'gcx, 'tcx>
     {
-        Sub { fields: f, a_is_expected: a_is_expected }
+        Sub { fields, a_is_expected, param_env }
     }
 
     fn with_expected_switched<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
@@ -64,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                                              -> RelateResult<'tcx, T>
     {
         match variance {
-            ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
+            ty::Invariant => self.fields.equate(self.param_env, self.a_is_expected).relate(a, b),
             ty::Covariant => self.relate(a, b),
             ty::Bivariant => Ok(a.clone()),
             ty::Contravariant => self.with_expected_switched(|this| { this.relate(b, a) }),
@@ -96,7 +99,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,
+                        self.param_env,
                         ty::Predicate::Subtype(
                             ty::Binder(ty::SubtypePredicate {
                                 a_is_expected: self.a_is_expected,
@@ -107,12 +110,19 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                 Ok(a)
             }
             (&ty::TyInfer(TyVar(a_id)), _) => {
-                self.fields
-                    .instantiate(b, RelationDir::SupertypeOf, a_id, !self.a_is_expected)?;
+                self.fields.instantiate(self.param_env,
+                                        b,
+                                        RelationDir::SupertypeOf,
+                                        a_id,
+                                        !self.a_is_expected)?;
                 Ok(a)
             }
             (_, &ty::TyInfer(TyVar(b_id))) => {
-                self.fields.instantiate(a, RelationDir::SubtypeOf, b_id, self.a_is_expected)?;
+                self.fields.instantiate(self.param_env,
+                                        a,
+                                        RelationDir::SubtypeOf,
+                                        b_id,
+                                        self.a_is_expected)?;
                 Ok(a)
             }
 
@@ -146,6 +156,6 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                   -> RelateResult<'tcx, ty::Binder<T>>
         where T: Relate<'tcx>
     {
-        self.fields.higher_ranked_sub(a, b, self.a_is_expected)
+        self.fields.higher_ranked_sub(self.param_env, a, b, self.a_is_expected)
     }
 }

From 5f72a06d465527d011b959db314ddde8758cfd7b Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 21 Aug 2017 10:31:00 -0400
Subject: [PATCH 21/63] track skol levels in the `ParamEnv` rather than via
 counter

---
 src/librustc/infer/higher_ranked/mod.rs    | 30 +++++-----
 src/librustc/infer/mod.rs                  | 21 +++----
 src/librustc/infer/region_inference/mod.rs | 65 ++++------------------
 src/librustc/traits/error_reporting.rs     |  3 +-
 src/librustc/traits/fulfill.rs             |  4 +-
 src/librustc/traits/mod.rs                 |  4 ++
 src/librustc/traits/project.rs             |  8 +--
 src/librustc/traits/select.rs              | 49 ++++++++--------
 src/librustc/ty/context.rs                 | 14 +++++
 9 files changed, 93 insertions(+), 105 deletions(-)

diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index d3c9f55ceffb7..6cb90c4491c6f 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -56,8 +56,8 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
 
             // First, we instantiate each bound region in the supertype with a
             // fresh concrete region.
-            let (b_prime, skol_map) =
-                self.infcx.skolemize_late_bound_regions(b, snapshot);
+            let (b_prime, param_env, skol_map) =
+                self.infcx.skolemize_late_bound_regions(param_env, b);
 
             // Second, we instantiate each bound region in the subtype with a fresh
             // region variable. These are declared in the innermost universe.
@@ -80,7 +80,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
 
             // We are finished with the skolemized regions now so pop
             // them off.
-            self.infcx.pop_skolemized(skol_map, snapshot);
+            self.infcx.pop_skolemized(param_env, skol_map, snapshot);
 
             debug!("higher_ranked_sub: OK result={:?}", result);
 
@@ -118,8 +118,8 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         return self.infcx.commit_if_ok(|snapshot| {
             // First, we instantiate each bound region in the matcher
             // with a skolemized region.
-            let ((a_match, a_value), skol_map) =
-                self.infcx.skolemize_late_bound_regions(a_pair, snapshot);
+            let ((a_match, a_value), param_env, skol_map) =
+                self.infcx.skolemize_late_bound_regions(param_env, a_pair);
 
             debug!("higher_ranked_match: a_match={:?}", a_match);
             debug!("higher_ranked_match: skol_map={:?}", skol_map);
@@ -199,7 +199,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             debug!("higher_ranked_match: value={:?}", a_value);
 
             // We are now done with these skolemized variables.
-            self.infcx.pop_skolemized(skol_map, snapshot);
+            self.infcx.pop_skolemized(param_env, skol_map, snapshot);
 
             Ok(HrMatchResult { value: a_value })
         });
@@ -589,13 +589,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     ///
     /// See `README.md` for more details.
     pub fn skolemize_late_bound_regions<T>(&self,
-                                           binder: &ty::Binder<T>,
-                                           snapshot: &CombinedSnapshot<'a, 'tcx>)
-                                           -> (T, SkolemizationMap<'tcx>)
+                                           mut param_env: ty::ParamEnv<'tcx>,
+                                           binder: &ty::Binder<T>)
+                                           -> (T, ty::ParamEnv<'tcx>, SkolemizationMap<'tcx>)
         where T : TypeFoldable<'tcx>
     {
         let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| {
-            self.region_vars.push_skolemized(br, &snapshot.region_vars_snapshot)
+            let (p, value) = self.tcx.mk_skolemized_region(param_env, br);
+            param_env = p;
+            value
         });
 
         debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})",
@@ -603,7 +605,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                result,
                map);
 
-        (result, map)
+        (result, param_env, map)
     }
 
     /// Searches the region constraints created since `snapshot` was started
@@ -689,6 +691,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// to the depth of the predicate, in this case 1, so that the final
     /// predicate is `for<'a> &'a int : Clone`.
     pub fn plug_leaks<T>(&self,
+                         param_env: ty::ParamEnv<'tcx>,
                          skol_map: SkolemizationMap<'tcx>,
                          snapshot: &CombinedSnapshot<'a, 'tcx>,
                          value: T) -> T
@@ -759,7 +762,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             }
         });
 
-        self.pop_skolemized(skol_map, snapshot);
+        self.pop_skolemized(param_env, skol_map, snapshot);
 
         debug!("plug_leaks: result={:?}", result);
 
@@ -775,11 +778,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     ///
     /// Note: popping also occurs implicitly as part of `leak_check`.
     pub fn pop_skolemized(&self,
+                          param_env: ty::ParamEnv<'tcx>,
                           skol_map: SkolemizationMap<'tcx>,
                           snapshot: &CombinedSnapshot<'a, 'tcx>) {
         debug!("pop_skolemized({:?})", skol_map);
         let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect();
-        self.region_vars.pop_skolemized(&skol_regions, &snapshot.region_vars_snapshot);
+        self.region_vars.pop_skolemized(param_env, &skol_regions, &snapshot.region_vars_snapshot);
         if !skol_map.is_empty() {
             self.projection_cache.borrow_mut().rollback_skolemized(
                 &snapshot.projection_cache_snapshot);
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 3a8333e1163de..b233440ab4f90 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -876,12 +876,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         -> InferResult<'tcx, ()>
     {
         self.commit_if_ok(|snapshot| {
-            let (ty::EquatePredicate(a, b), skol_map) =
-                self.skolemize_late_bound_regions(predicate, snapshot);
+            let (ty::EquatePredicate(a, b), param_env, skol_map) =
+                self.skolemize_late_bound_regions(param_env, predicate);
             let cause_span = cause.span;
             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);
+            self.pop_skolemized(param_env, skol_map, snapshot);
             Ok(eqty_ok.unit())
         })
     }
@@ -913,31 +913,32 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
 
         Some(self.commit_if_ok(|snapshot| {
-            let (ty::SubtypePredicate { a_is_expected, a, b}, skol_map) =
-                self.skolemize_late_bound_regions(predicate, snapshot);
+            let (ty::SubtypePredicate { a_is_expected, a, b}, param_env, skol_map) =
+                self.skolemize_late_bound_regions(param_env, predicate);
 
             let cause_span = cause.span;
             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);
+            self.pop_skolemized(param_env, skol_map, snapshot);
             Ok(ok.unit())
         }))
     }
 
     pub fn region_outlives_predicate(&self,
                                      cause: &traits::ObligationCause<'tcx>,
+                                     param_env: ty::ParamEnv<'tcx>,
                                      predicate: &ty::PolyRegionOutlivesPredicate<'tcx>)
-        -> UnitResult<'tcx>
+                                     -> UnitResult<'tcx>
     {
         self.commit_if_ok(|snapshot| {
-            let (ty::OutlivesPredicate(r_a, r_b), skol_map) =
-                self.skolemize_late_bound_regions(predicate, snapshot);
+            let (ty::OutlivesPredicate(r_a, r_b), param_env, skol_map) =
+                self.skolemize_late_bound_regions(param_env, predicate);
             let origin =
                 SubregionOrigin::from_obligation_cause(cause,
                                                        || RelateRegionParamBound(cause.span));
             self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
             self.leak_check(false, cause.span, &skol_map, snapshot)?;
-            Ok(self.pop_skolemized(skol_map, snapshot))
+            Ok(self.pop_skolemized(param_env, skol_map, snapshot))
         })
     }
 
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 1fa114f9e751c..fc6b073d01f13 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -223,7 +223,6 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     lubs: RefCell<CombineMap<'tcx>>,
     glbs: RefCell<CombineMap<'tcx>>,
-    skolemization_count: Cell<ty::UniverseIndex>,
     bound_count: Cell<u32>,
 
     /// The undo log records actions that might later be undone.
@@ -247,7 +246,6 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 pub struct RegionSnapshot {
     length: usize,
     region_snapshot: ut::Snapshot<ut::InPlace<ty::RegionVid>>,
-    skolemization_count: ty::UniverseIndex,
 }
 
 /// When working with skolemized regions, we often wish to find all of
@@ -369,7 +367,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             givens: RefCell::new(FxHashSet()),
             lubs: RefCell::new(FxHashMap()),
             glbs: RefCell::new(FxHashMap()),
-            skolemization_count: Cell::new(ty::UniverseIndex::ROOT),
             bound_count: Cell::new(0),
             undo_log: RefCell::new(Vec::new()),
             unification_table: RefCell::new(ut::UnificationTable::new()),
@@ -387,7 +384,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         RegionSnapshot {
             length,
             region_snapshot: self.unification_table.borrow_mut().snapshot(),
-            skolemization_count: self.skolemization_count.get(),
         }
     }
 
@@ -395,10 +391,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         debug!("RegionVarBindings: commit({})", snapshot.length);
         assert!(self.undo_log.borrow().len() > snapshot.length);
         assert!((*self.undo_log.borrow())[snapshot.length] == OpenSnapshot);
-        assert!(self.skolemization_count.get() == snapshot.skolemization_count,
-                "failed to pop skolemized regions: {:?} now vs {:?} at start",
-                self.skolemization_count.get(),
-                snapshot.skolemization_count);
 
         let mut undo_log = self.undo_log.borrow_mut();
         if snapshot.length == 0 {
@@ -419,7 +411,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         }
         let c = undo_log.pop().unwrap();
         assert!(c == OpenSnapshot);
-        self.skolemization_count.set(snapshot.skolemization_count);
         self.unification_table.borrow_mut()
             .rollback_to(snapshot.region_snapshot);
     }
@@ -494,61 +485,29 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         self.var_infos.borrow()[vid.index as usize].origin.clone()
     }
 
-    /// Creates a new skolemized region. Skolemized regions are fresh
-    /// regions used when performing higher-ranked computations. They
-    /// must be used in a very particular way and are never supposed
-    /// to "escape" out into error messages or the code at large.
-    ///
-    /// The idea is to always create a snapshot. Skolemized regions
-    /// can be created in the context of this snapshot, but before the
-    /// snapshot is committed or rolled back, they must be popped
-    /// (using `pop_skolemized_regions`), so that their numbers can be
-    /// recycled. Normally you don't have to think about this: you use
-    /// the APIs in `higher_ranked/mod.rs`, such as
-    /// `skolemize_late_bound_regions` and `plug_leaks`, which will
-    /// guide you on this path (ensure that the `SkolemizationMap` is
-    /// consumed and you are good).  There are also somewhat extensive
-    /// comments in `higher_ranked/README.md`.
-    ///
-    /// The `snapshot` argument to this function is not really used;
-    /// it's just there to make it explicit which snapshot bounds the
-    /// skolemized region that results. It should always be the top-most snapshot.
-    pub fn push_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot)
-                           -> Region<'tcx> {
-        assert!(self.in_snapshot());
-        assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot);
-
-        let universe = self.skolemization_count.get().subuniverse();
-        self.skolemization_count.set(universe);
-        self.tcx.mk_region(ReSkolemized(universe, br))
-    }
-
     /// Removes all the edges to/from the skolemized regions that are
     /// in `skols`. This is used after a higher-ranked operation
     /// completes to remove all trace of the skolemized regions
     /// created in that time.
     pub fn pop_skolemized(&self,
+                          param_env: ty::ParamEnv<'tcx>,
                           skols: &FxHashSet<ty::Region<'tcx>>,
                           snapshot: &RegionSnapshot) {
-        debug!("pop_skolemized_regions(skols={:?})", skols);
+        debug!("pop_skolemized_regions(param_env.universe={:?}, skols={:?})",
+               param_env.universe,
+               skols);
 
         assert!(self.in_snapshot());
         assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot);
-        assert!(self.skolemization_count.get().as_usize() >= skols.len(),
+        assert!(param_env.universe.as_usize() >= skols.len(),
                 "popping more skolemized variables than actually exist, \
-                 sc now = {}, skols.len = {}",
-                self.skolemization_count.get().as_usize(),
+                 universe now = {:?}, skols.len = {}",
+                param_env.universe,
                 skols.len());
 
-        let last_to_pop = self.skolemization_count.get().subuniverse();
+        let last_to_pop = param_env.universe.subuniverse();
         let first_to_pop = ty::UniverseIndex::from(last_to_pop.as_u32() - (skols.len() as u32));
 
-        assert!(first_to_pop >= snapshot.skolemization_count,
-                "popping more regions than snapshot contains, \
-                 sc now = {:?}, sc then = {:?}, skols.len = {}",
-                self.skolemization_count.get(),
-                snapshot.skolemization_count,
-                skols.len());
         debug_assert! {
             skols.iter()
                  .all(|&k| match *k {
@@ -559,8 +518,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                          false
                  }),
             "invalid skolemization keys or keys out of range ({:?}..{:?}): {:?}",
-            snapshot.skolemization_count,
-            self.skolemization_count.get(),
+            first_to_pop,
+            last_to_pop,
             skols
         }
 
@@ -579,7 +538,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             self.rollback_undo_entry(undo_entry);
         }
 
-        self.skolemization_count.set(snapshot.skolemization_count);
         return;
 
         fn kill_constraint<'tcx>(skols: &FxHashSet<ty::Region<'tcx>>,
@@ -1594,8 +1552,7 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
 
 impl fmt::Debug for RegionSnapshot {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "RegionSnapshot(length={},skolemization={:?})",
-               self.length, self.skolemization_count)
+        write!(f, "RegionSnapshot(length={})", self.length)
     }
 }
 
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 4458f30394d5e..4a6305e9bf807 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -636,7 +636,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     ty::Predicate::RegionOutlives(ref predicate) => {
                         let predicate = self.resolve_type_vars_if_possible(predicate);
                         let err = self.region_outlives_predicate(&obligation.cause,
-                                                                    &predicate).err().unwrap();
+                                                                 obligation.param_env,
+                                                                 &predicate).err().unwrap();
                         struct_span_err!(self.tcx.sess, span, E0279,
                             "the requirement `{}` is not satisfied (`{}`)",
                             predicate, err)
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index cc2506d1afc50..cd48417e4a2da 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -427,7 +427,9 @@ fn process_predicate<'a, 'gcx, 'tcx>(
         }
 
         ty::Predicate::RegionOutlives(ref binder) => {
-            match selcx.infcx().region_outlives_predicate(&obligation.cause, binder) {
+            match selcx.infcx().region_outlives_predicate(&obligation.cause,
+                                                          obligation.param_env,
+                                                          binder) {
                 Ok(()) => Ok(Some(Vec::new())),
                 Err(_) => Err(CodeSelectionError(Unimplemented)),
             }
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 09c7d7316baa2..b464f0ca06145 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -747,6 +747,10 @@ impl<'tcx,O> Obligation<'tcx,O> {
                      recursion_depth: self.recursion_depth,
                      predicate: value }
     }
+
+    pub fn with_env(self, param_env: ty::ParamEnv<'tcx>) -> Obligation<'tcx, O> {
+        Obligation { param_env, ..self }
+    }
 }
 
 impl<'tcx> ObligationCause<'tcx> {
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 6cb556cc99aa5..e9168b68336a2 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -135,15 +135,15 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
 
     let infcx = selcx.infcx();
     infcx.commit_if_ok(|snapshot| {
-        let (skol_predicate, skol_map) =
-            infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot);
+        let (skol_predicate, param_env, skol_map) =
+            infcx.skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
 
-        let skol_obligation = obligation.with(skol_predicate);
+        let skol_obligation = obligation.with(skol_predicate).with_env(param_env);
         let r = match project_and_unify_type(selcx, &skol_obligation) {
             Ok(result) => {
                 let span = obligation.cause.span;
                 match infcx.leak_check(false, span, &skol_map, snapshot) {
-                    Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, result)),
+                    Ok(()) => Ok(infcx.plug_leaks(param_env, skol_map, snapshot, result)),
                     Err(e) => Err(MismatchedProjectionTypes { err: e }),
                 }
             }
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index ec911911a07ba..dad871dba2ce8 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -1412,13 +1412,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     {
         let poly_trait_predicate =
             self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
-        let (skol_trait_predicate, skol_map) =
-            self.infcx().skolemize_late_bound_regions(&poly_trait_predicate, snapshot);
+        let (skol_trait_predicate, param_env, skol_map) =
+            self.infcx().skolemize_late_bound_regions(obligation.param_env, &poly_trait_predicate);
         debug!("match_projection_obligation_against_definition_bounds: \
                 skol_trait_predicate={:?} skol_map={:?}",
                skol_trait_predicate,
                skol_map);
 
+        let obligation = obligation.clone().with_env(param_env);
+
         let (def_id, substs) = match skol_trait_predicate.trait_ref.self_ty().sty {
             ty::TyProjection(ref data) =>
                 (data.trait_ref(self.tcx()).def_id, data.substs),
@@ -1446,7 +1448,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             .filter_to_traits()
             .find(
                 |bound| self.probe(
-                    |this, _| this.match_projection(obligation,
+                    |this, _| this.match_projection(&obligation,
                                                     bound.clone(),
                                                     skol_trait_predicate.trait_ref.clone(),
                                                     &skol_map,
@@ -1459,13 +1461,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             None => false,
             Some(bound) => {
                 // Repeat the successful match, if any, this time outside of a probe.
-                let result = self.match_projection(obligation,
+                let result = self.match_projection(&obligation,
                                                    bound,
                                                    skol_trait_predicate.trait_ref.clone(),
                                                    &skol_map,
                                                    snapshot);
 
-                self.infcx.pop_skolemized(skol_map, snapshot);
+                self.infcx.pop_skolemized(param_env, skol_map, snapshot);
 
                 assert!(result);
                 true
@@ -2234,8 +2236,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             let ty: ty::Binder<Ty<'tcx>> = ty::Binder(ty); // <----------/
 
             self.in_snapshot(|this, snapshot| {
-                let (skol_ty, skol_map) =
-                    this.infcx().skolemize_late_bound_regions(&ty, snapshot);
+                let (skol_ty, param_env, skol_map) =
+                    this.infcx().skolemize_late_bound_regions(param_env, &ty);
                 let Normalized { value: normalized_ty, mut obligations } =
                     project::normalize_with_depth(this,
                                                   param_env,
@@ -2250,7 +2252,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                                        normalized_ty,
                                                        &[]);
                 obligations.push(skol_obligation);
-                this.infcx().plug_leaks(skol_map, snapshot, obligations)
+                this.infcx().plug_leaks(param_env, skol_map, snapshot, obligations)
             })
         }).collect()
     }
@@ -2451,12 +2453,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let trait_obligations = self.in_snapshot(|this, snapshot| {
             let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
-            let (trait_ref, skol_map) =
-                this.infcx().skolemize_late_bound_regions(&poly_trait_ref, snapshot);
+            let (trait_ref, param_env, skol_map) =
+                this.infcx().skolemize_late_bound_regions(obligation.param_env, &poly_trait_ref);
             let cause = obligation.derived_cause(ImplDerivedObligation);
             this.impl_or_trait_obligations(cause,
                                            obligation.recursion_depth + 1,
-                                           obligation.param_env,
+                                           param_env,
                                            trait_def_id,
                                            &trait_ref.substs,
                                            skol_map,
@@ -2485,7 +2487,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // First, create the substitutions by matching the impl again,
         // this time not in a probe.
         self.in_snapshot(|this, snapshot| {
-            let (substs, skol_map) =
+            let (substs, param_env, skol_map) =
                 this.rematch_impl(impl_def_id, obligation,
                                   snapshot);
             debug!("confirm_impl_candidate substs={:?}", substs);
@@ -2494,7 +2496,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                              substs,
                              cause,
                              obligation.recursion_depth + 1,
-                             obligation.param_env,
+                             param_env,
                              skol_map,
                              snapshot)
         })
@@ -2998,10 +3000,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     obligation: &TraitObligation<'tcx>,
                     snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
                     -> (Normalized<'tcx, &'tcx Substs<'tcx>>,
+                        ty::ParamEnv<'tcx>,
                         infer::SkolemizationMap<'tcx>)
     {
         match self.match_impl(impl_def_id, obligation, snapshot) {
-            Ok((substs, skol_map)) => (substs, skol_map),
+            Ok(tuple) => tuple,
             Err(()) => {
                 bug!("Impl {:?} was matchable against {:?} but now is not",
                      impl_def_id,
@@ -3015,6 +3018,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                   obligation: &TraitObligation<'tcx>,
                   snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
                   -> Result<(Normalized<'tcx, &'tcx Substs<'tcx>>,
+                             ty::ParamEnv<'tcx>,
                              infer::SkolemizationMap<'tcx>), ()>
     {
         let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
@@ -3026,12 +3030,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             return Err(());
         }
 
-        let (skol_obligation, skol_map) = self.infcx().skolemize_late_bound_regions(
-            &obligation.predicate,
-            snapshot);
+        let (skol_obligation, param_env, skol_map) =
+             self.infcx().skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
         let skol_obligation_trait_ref = skol_obligation.trait_ref;
 
-        let impl_substs = self.infcx.fresh_substs_for_item(obligation.param_env.universe,
+        let impl_substs = self.infcx.fresh_substs_for_item(param_env.universe,
                                                            obligation.cause.span,
                                                            impl_def_id);
 
@@ -3040,7 +3043,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let impl_trait_ref =
             project::normalize_with_depth(self,
-                                          obligation.param_env,
+                                          param_env,
                                           obligation.cause.clone(),
                                           obligation.recursion_depth + 1,
                                           &impl_trait_ref);
@@ -3053,7 +3056,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                skol_obligation_trait_ref);
 
         let InferOk { obligations, .. } =
-            self.infcx.at(&obligation.cause, obligation.param_env)
+            self.infcx.at(&obligation.cause, param_env)
                       .eq(skol_obligation_trait_ref, impl_trait_ref.value)
                       .map_err(|e| {
                           debug!("match_impl: failed eq_trait_refs due to `{}`", e);
@@ -3073,7 +3076,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         Ok((Normalized {
             value: impl_substs,
             obligations: impl_trait_ref.obligations
-        }, skol_map))
+        },
+            param_env,
+            skol_map))
     }
 
     fn fast_reject_trait_refs(&mut self,
@@ -3244,7 +3249,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     predicate: predicate.value
                 }))
         }).collect();
-        self.infcx().plug_leaks(skol_map, snapshot, predicates)
+        self.infcx().plug_leaks(param_env, skol_map, snapshot, predicates)
     }
 }
 
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 37c4346a7dc93..b8efca0faf3d4 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1308,6 +1308,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    /// Creates a new skolemized region in the given parameter
+    /// environment. Returns a new parameter environment and the
+    /// resulting skolemized region. The new environment has an
+    /// increased universe index in which the new region is visible.
+    pub fn mk_skolemized_region(self,
+                                param_env: ty::ParamEnv<'tcx>,
+                                br: ty::BoundRegion)
+                                -> (ty::ParamEnv<'tcx>, ty::Region<'tcx>) {
+        let universe = param_env.universe.subuniverse();
+        let region = self.mk_region(ty::ReSkolemized(universe, br));
+        let param_env = ty::ParamEnv { universe, ..param_env };
+        (param_env, region)
+    }
+
     pub fn serialize_query_result_cache<E>(self,
                                            encoder: &mut E)
                                            -> Result<(), E::Error>

From 582ff8731364c46cea1d53f0317b5c541ec5f4bb Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 23 Aug 2017 09:19:18 -0400
Subject: [PATCH 22/63] remove `hr_match` -- no longer needed

---
 src/librustc/infer/higher_ranked/mod.rs | 122 ------------------------
 src/librustc/infer/mod.rs               |  34 -------
 src/librustc/traits/project.rs          |  25 +++--
 3 files changed, 17 insertions(+), 164 deletions(-)

diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index 6cb90c4491c6f..53db1acfdd8f2 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -14,7 +14,6 @@
 use super::{CombinedSnapshot,
             InferCtxt,
             HigherRankedType,
-            SubregionOrigin,
             SkolemizationMap};
 use super::combine::CombineFields;
 use super::region_inference::{TaintDirections};
@@ -25,10 +24,6 @@ use ty::relate::{Relate, RelateResult, TypeRelation};
 use syntax_pos::Span;
 use util::nodemap::{FxHashMap, FxHashSet};
 
-pub struct HrMatchResult<U> {
-    pub value: U,
-}
-
 impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
     pub fn higher_ranked_sub<T>(&mut self,
                                 param_env: ty::ParamEnv<'tcx>,
@@ -88,123 +83,6 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         });
     }
 
-    /// The value consists of a pair `(t, u)` where `t` is the
-    /// *matcher* and `u` is a *value*. The idea is to find a
-    /// substitution `S` such that `S(t) == b`, and then return
-    /// `S(u)`. In other words, find values for the late-bound regions
-    /// in `a` that can make `t == b` and then replace the LBR in `u`
-    /// with those values.
-    ///
-    /// This routine is (as of this writing) used in trait matching,
-    /// particularly projection.
-    ///
-    /// NB. It should not happen that there are LBR appearing in `U`
-    /// that do not appear in `T`. If that happens, those regions are
-    /// unconstrained, and this routine replaces them with `'static`.
-    pub fn higher_ranked_match<T, U>(&mut self,
-                                     param_env: ty::ParamEnv<'tcx>,
-                                     a_pair: &Binder<(T, U)>,
-                                     b_match: &T,
-                                     a_is_expected: bool)
-                                     -> RelateResult<'tcx, HrMatchResult<U>>
-        where T: Relate<'tcx>,
-              U: TypeFoldable<'tcx>
-    {
-        debug!("higher_ranked_match(a={:?}, b={:?})",
-               a_pair, b_match);
-
-        // Start a snapshot so we can examine "all bindings that were
-        // created as part of this type comparison".
-        return self.infcx.commit_if_ok(|snapshot| {
-            // First, we instantiate each bound region in the matcher
-            // with a skolemized region.
-            let ((a_match, a_value), param_env, skol_map) =
-                self.infcx.skolemize_late_bound_regions(param_env, a_pair);
-
-            debug!("higher_ranked_match: a_match={:?}", a_match);
-            debug!("higher_ranked_match: skol_map={:?}", skol_map);
-
-            // Equate types now that bound regions have been replaced.
-            self.equate(param_env, a_is_expected).relate(&a_match, &b_match)?;
-
-            // Map each skolemized region to a vector of other regions that it
-            // must be equated with. (Note that this vector may include other
-            // skolemized regions from `skol_map`.)
-            let skol_resolution_map: FxHashMap<_, _> =
-                skol_map
-                .iter()
-                .map(|(&br, &skol)| {
-                    let tainted_regions =
-                        self.infcx.tainted_regions(snapshot,
-                                                   skol,
-                                                   TaintDirections::incoming()); // [1]
-
-                    // [1] this routine executes after the skolemized
-                    // regions have been *equated* with something
-                    // else, so examining the incoming edges ought to
-                    // be enough to collect all constraints
-
-                    (skol, (br, tainted_regions))
-                })
-                .collect();
-
-            // For each skolemized region, pick a representative -- which can
-            // be any region from the sets above, except for other members of
-            // `skol_map`. There should always be a representative if things
-            // are properly well-formed.
-            let skol_representatives: FxHashMap<_, _> =
-                skol_resolution_map
-                .iter()
-                .map(|(&skol, &(_, ref regions))| {
-                    let representative =
-                        regions.iter()
-                               .filter(|&&r| !skol_resolution_map.contains_key(r))
-                               .cloned()
-                               .next()
-                               .unwrap_or_else(|| {
-                                   bug!("no representative region for `{:?}` in `{:?}`",
-                                        skol, regions)
-                               });
-
-                    (skol, representative)
-                })
-                .collect();
-
-            // Equate all the members of each skolemization set with the
-            // representative.
-            for (skol, &(_br, ref regions)) in &skol_resolution_map {
-                let representative = &skol_representatives[skol];
-                debug!("higher_ranked_match: \
-                        skol={:?} representative={:?} regions={:?}",
-                       skol, representative, regions);
-                for region in regions.iter()
-                                     .filter(|&r| !skol_resolution_map.contains_key(r))
-                                     .filter(|&r| r != representative)
-                {
-                    let origin = SubregionOrigin::Subtype(self.trace.clone());
-                    self.infcx.region_vars.make_eqregion(origin,
-                                                         *representative,
-                                                         *region);
-                }
-            }
-
-            // Replace the skolemized regions appearing in value with
-            // their representatives
-            let a_value =
-                fold_regions_in(
-                    self.tcx(),
-                    &a_value,
-                    |r, _| skol_representatives.get(&r).cloned().unwrap_or(r));
-
-            debug!("higher_ranked_match: value={:?}", a_value);
-
-            // We are now done with these skolemized variables.
-            self.infcx.pop_skolemized(param_env, skol_map, snapshot);
-
-            Ok(HrMatchResult { value: a_value })
-        });
-    }
-
     pub fn higher_ranked_lub<T>(&mut self,
                                 param_env: ty::ParamEnv<'tcx>,
                                 a: &Binder<T>,
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index b233440ab4f90..edd436d067141 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -40,7 +40,6 @@ use util::nodemap::FxHashMap;
 use arena::DroplessArena;
 
 use self::combine::CombineFields;
-use self::higher_ranked::HrMatchResult;
 use self::region_inference::{RegionVarBindings, RegionSnapshot};
 use self::type_variable::TypeVariableOrigin;
 use self::unify_key::ToType;
@@ -1236,39 +1235,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             |br| self.next_region_var(universe, LateBoundRegion(span, br, lbrct)))
     }
 
-    /// Given a higher-ranked projection predicate like:
-    ///
-    ///     for<'a> <T as Fn<&'a u32>>::Output = &'a u32
-    ///
-    /// and a target trait-ref like:
-    ///
-    ///     <T as Fn<&'x u32>>
-    ///
-    /// find a substitution `S` for the higher-ranked regions (here,
-    /// `['a => 'x]`) such that the predicate matches the trait-ref,
-    /// and then return the value (here, `&'a u32`) but with the
-    /// substitution applied (hence, `&'x u32`).
-    ///
-    /// See `higher_ranked_match` in `higher_ranked/mod.rs` for more
-    /// 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>>>
-    {
-        let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref(self.tcx), p.ty));
-        let trace = TypeTrace {
-            cause,
-            values: TraitRefs(ExpectedFound::new(true, match_pair.skip_binder().0, match_b))
-        };
-
-        let mut combine = self.combine_fields(trace);
-        let result = combine.higher_ranked_match(param_env, &match_pair, &match_b, true)?;
-        Ok(InferOk { value: result, obligations: combine.obligations })
-    }
-
     /// See `verify_generic_bound` method in `region_inference`
     pub fn verify_generic_bound(&self,
                                 origin: SubregionOrigin<'tcx>,
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index e9168b68336a2..fc45485cec40b 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -25,7 +25,7 @@ use super::VtableImplData;
 use super::util;
 
 use hir::def_id::DefId;
-use infer::{InferCtxt, InferOk};
+use infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
 use infer::type_variable::TypeVariableOrigin;
 use middle::const_val::ConstVal;
 use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
@@ -1405,17 +1405,26 @@ fn confirm_callable_candidate<'cx, 'gcx, 'tcx>(
 fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    poly_projection: ty::PolyProjectionPredicate<'tcx>)
+    poly_cache_entry: ty::PolyProjectionPredicate<'tcx>)
     -> Progress<'tcx>
 {
     let infcx = selcx.infcx();
-    let cause = obligation.cause.clone();
+    let cause = &obligation.cause;
     let param_env = obligation.param_env;
-    let trait_ref = obligation.predicate.trait_ref(infcx.tcx);
-    match infcx.match_poly_projection_predicate(cause, param_env, poly_projection, trait_ref) {
-        Ok(InferOk { value: ty_match, obligations }) => {
+
+    let (cache_entry, _skol_map) =
+        infcx.replace_late_bound_regions_with_fresh_var(
+            cause.span,
+            param_env.universe,
+            LateBoundRegionConversionTime::HigherRankedType,
+            &poly_cache_entry);
+
+    let cache_trait_ref = cache_entry.projection_ty.trait_ref(infcx.tcx);
+    let obligation_trait_ref = obligation.predicate.trait_ref(infcx.tcx);
+    match infcx.at(cause, param_env).eq(cache_trait_ref, obligation_trait_ref) {
+        Ok(InferOk { value: _, obligations }) => {
             Progress {
-                ty: ty_match.value,
+                ty: cache_entry.ty,
                 obligations,
             }
         }
@@ -1425,7 +1434,7 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
                 "Failed to unify obligation `{:?}` \
                  with poly_projection `{:?}`: {:?}",
                 obligation,
-                poly_projection,
+                poly_cache_entry,
                 e);
         }
     }

From b02f6c9fa2cadf4bf021178e1f39da3bfcee7c40 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 23 Aug 2017 16:21:55 -0400
Subject: [PATCH 23/63] remove `pop_skolemized` and friends

---
 src/librustc/infer/higher_ranked/mod.rs       | 202 +-----------------
 src/librustc/infer/mod.rs                     |  31 +--
 src/librustc/traits/project.rs                |  19 +-
 src/librustc/traits/select.rs                 | 107 +++-------
 .../compile-fail/associated-types-eq-hr.rs    |  20 +-
 .../coherence-different-binding.rs}           |   6 +-
 6 files changed, 69 insertions(+), 316 deletions(-)
 rename src/test/{run-pass/coherence-subtyping.rs => compile-fail/coherence-different-binding.rs} (79%)

diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index 53db1acfdd8f2..d798414c268d5 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -19,7 +19,6 @@ use super::combine::CombineFields;
 use super::region_inference::{TaintDirections};
 
 use ty::{self, TyCtxt, Binder, TypeFoldable};
-use ty::error::TypeError;
 use ty::relate::{Relate, RelateResult, TypeRelation};
 use syntax_pos::Span;
 use util::nodemap::{FxHashMap, FxHashSet};
@@ -46,12 +45,12 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
 
         // Start a snapshot so we can examine "all bindings that were
         // created as part of this type comparison".
-        return self.infcx.commit_if_ok(|snapshot| {
+        return self.infcx.commit_if_ok(|_snapshot| {
             let span = self.trace.cause.span;
 
             // First, we instantiate each bound region in the supertype with a
             // fresh concrete region.
-            let (b_prime, param_env, skol_map) =
+            let (b_prime, param_env, _skol_map) =
                 self.infcx.skolemize_late_bound_regions(param_env, b);
 
             // Second, we instantiate each bound region in the subtype with a fresh
@@ -69,14 +68,6 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
             // Compare types now that bound regions have been replaced.
             let result = self.sub(param_env, a_is_expected).relate(&a_prime, &b_prime)?;
 
-            // Presuming type comparison succeeds, we need to check
-            // that the skolemized regions do not "leak".
-            self.infcx.leak_check(!a_is_expected, span, &skol_map, snapshot)?;
-
-            // We are finished with the skolemized regions now so pop
-            // them off.
-            self.infcx.pop_skolemized(param_env, skol_map, snapshot);
-
             debug!("higher_ranked_sub: OK result={:?}", result);
 
             Ok(ty::Binder(result))
@@ -458,13 +449,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// skolemized region. This is the first step of checking subtyping
     /// when higher-ranked things are involved.
     ///
-    /// **Important:** you must call this function from within a snapshot.
-    /// Moreover, before committing the snapshot, you must eventually call
-    /// either `plug_leaks` or `pop_skolemized` to remove the skolemized
-    /// regions. If you rollback the snapshot (or are using a probe), then
-    /// the pop occurs as part of the rollback, so an explicit call is not
-    /// needed (but is also permitted).
-    ///
     /// See `README.md` for more details.
     pub fn skolemize_late_bound_regions<T>(&self,
                                            mut param_env: ty::ParamEnv<'tcx>,
@@ -485,186 +469,4 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         (result, param_env, map)
     }
-
-    /// Searches the region constraints created since `snapshot` was started
-    /// and checks to determine whether any of the skolemized regions created
-    /// in `skol_map` would "escape" -- meaning that they are related to
-    /// other regions in some way. If so, the higher-ranked subtyping doesn't
-    /// hold. See `README.md` for more details.
-    pub fn leak_check(&self,
-                      overly_polymorphic: bool,
-                      _span: Span,
-                      skol_map: &SkolemizationMap<'tcx>,
-                      snapshot: &CombinedSnapshot<'a, 'tcx>)
-                      -> RelateResult<'tcx, ()>
-    {
-        debug!("leak_check: skol_map={:?}",
-               skol_map);
-
-        let new_vars = self.region_vars_confined_to_snapshot(snapshot);
-        for (&skol_br, &skol) in skol_map {
-            // The inputs to a skolemized variable can only
-            // be itself or other new variables.
-            let incoming_taints = self.tainted_regions(snapshot,
-                                                       skol,
-                                                       TaintDirections::both());
-            for &tainted_region in &incoming_taints {
-                // Each skolemized should only be relatable to itself
-                // or new variables:
-                match *tainted_region {
-                    ty::ReVar(vid) => {
-                        if new_vars.contains(&vid) {
-                            continue;
-                        }
-                    }
-                    _ => {
-                        if tainted_region == skol { continue; }
-                    }
-                };
-
-                debug!("{:?} (which replaced {:?}) is tainted by {:?}",
-                       skol,
-                       skol_br,
-                       tainted_region);
-
-                return Err(if overly_polymorphic {
-                    debug!("Overly polymorphic!");
-                    TypeError::RegionsOverlyPolymorphic(skol_br, tainted_region)
-                } else {
-                    debug!("Not as polymorphic!");
-                    TypeError::RegionsInsufficientlyPolymorphic(skol_br, tainted_region)
-                })
-            }
-        }
-
-        Ok(())
-    }
-
-    /// This code converts from skolemized regions back to late-bound
-    /// regions. It works by replacing each region in the taint set of a
-    /// skolemized region with a bound-region. The bound region will be bound
-    /// by the outer-most binder in `value`; the caller must ensure that there is
-    /// such a binder and it is the right place.
-    ///
-    /// This routine is only intended to be used when the leak-check has
-    /// passed; currently, it's used in the trait matching code to create
-    /// a set of nested obligations frmo an impl that matches against
-    /// something higher-ranked.  More details can be found in
-    /// `librustc/middle/traits/README.md`.
-    ///
-    /// As a brief example, consider the obligation `for<'a> Fn(&'a int)
-    /// -> &'a int`, and the impl:
-    ///
-    ///     impl<A,R> Fn<A,R> for SomethingOrOther
-    ///         where A : Clone
-    ///     { ... }
-    ///
-    /// Here we will have replaced `'a` with a skolemized region
-    /// `'0`. This means that our substitution will be `{A=>&'0
-    /// int, R=>&'0 int}`.
-    ///
-    /// When we apply the substitution to the bounds, we will wind up with
-    /// `&'0 int : Clone` as a predicate. As a last step, we then go and
-    /// replace `'0` with a late-bound region `'a`.  The depth is matched
-    /// to the depth of the predicate, in this case 1, so that the final
-    /// predicate is `for<'a> &'a int : Clone`.
-    pub fn plug_leaks<T>(&self,
-                         param_env: ty::ParamEnv<'tcx>,
-                         skol_map: SkolemizationMap<'tcx>,
-                         snapshot: &CombinedSnapshot<'a, 'tcx>,
-                         value: T) -> T
-        where T : TypeFoldable<'tcx>
-    {
-        debug!("plug_leaks(skol_map={:?}, value={:?})",
-               skol_map,
-               value);
-
-        if skol_map.is_empty() {
-            return value;
-        }
-
-        // Compute a mapping from the "taint set" of each skolemized
-        // region back to the `ty::BoundRegion` that it originally
-        // represented. Because `leak_check` passed, we know that
-        // these taint sets are mutually disjoint.
-        let inv_skol_map: FxHashMap<ty::Region<'tcx>, ty::BoundRegion> =
-            skol_map
-            .iter()
-            .flat_map(|(&skol_br, &skol)| {
-                self.tainted_regions(snapshot, skol, TaintDirections::both())
-                    .into_iter()
-                    .map(move |tainted_region| (tainted_region, skol_br))
-            })
-            .collect();
-
-        debug!("plug_leaks: inv_skol_map={:?}",
-               inv_skol_map);
-
-        // Remove any instantiated type variables from `value`; those can hide
-        // references to regions from the `fold_regions` code below.
-        let value = self.resolve_type_vars_if_possible(&value);
-
-        // Map any skolemization byproducts back to a late-bound
-        // region. Put that late-bound region at whatever the outermost
-        // binder is that we encountered in `value`. The caller is
-        // responsible for ensuring that (a) `value` contains at least one
-        // binder and (b) that binder is the one we want to use.
-        let result = self.tcx.fold_regions(&value, &mut false, |r, current_depth| {
-            match inv_skol_map.get(&r) {
-                None => r,
-                Some(br) => {
-                    // It is the responsibility of the caller to ensure
-                    // that each skolemized region appears within a
-                    // binder. In practice, this routine is only used by
-                    // trait checking, and all of the skolemized regions
-                    // appear inside predicates, which always have
-                    // binders, so this assert is satisfied.
-                    assert!(current_depth > 1);
-
-                    // since leak-check passed, this skolemized region
-                    // should only have incoming edges from variables
-                    // (which ought not to escape the snapshot, but we
-                    // don't check that) or itself
-                    assert!(
-                        match *r {
-                            ty::ReVar(_) => true,
-                            ty::ReSkolemized(_, ref br1) => br == br1,
-                            _ => false,
-                        },
-                        "leak-check would have us replace {:?} with {:?}",
-                        r, br);
-
-                    self.tcx.mk_region(ty::ReLateBound(
-                        ty::DebruijnIndex::new(current_depth - 1), br.clone()))
-                }
-            }
-        });
-
-        self.pop_skolemized(param_env, skol_map, snapshot);
-
-        debug!("plug_leaks: result={:?}", result);
-
-        result
-    }
-
-    /// Pops the skolemized regions found in `skol_map` from the region
-    /// inference context. Whenever you create skolemized regions via
-    /// `skolemize_late_bound_regions`, they must be popped before you
-    /// commit the enclosing snapshot (if you do not commit, e.g. within a
-    /// probe or as a result of an error, then this is not necessary, as
-    /// popping happens as part of the rollback).
-    ///
-    /// Note: popping also occurs implicitly as part of `leak_check`.
-    pub fn pop_skolemized(&self,
-                          param_env: ty::ParamEnv<'tcx>,
-                          skol_map: SkolemizationMap<'tcx>,
-                          snapshot: &CombinedSnapshot<'a, 'tcx>) {
-        debug!("pop_skolemized({:?})", skol_map);
-        let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect();
-        self.region_vars.pop_skolemized(param_env, &skol_regions, &snapshot.region_vars_snapshot);
-        if !skol_map.is_empty() {
-            self.projection_cache.borrow_mut().rollback_skolemized(
-                &snapshot.projection_cache_snapshot);
-        }
-    }
 }
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index edd436d067141..035fcdad9410e 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -874,13 +874,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                               predicate: &ty::PolyEquatePredicate<'tcx>)
         -> InferResult<'tcx, ()>
     {
-        self.commit_if_ok(|snapshot| {
-            let (ty::EquatePredicate(a, b), param_env, skol_map) =
+        self.commit_if_ok(|_snapshot| {
+            let (ty::EquatePredicate(a, b), param_env, _skol_map) =
                 self.skolemize_late_bound_regions(param_env, predicate);
-            let cause_span = cause.span;
             let eqty_ok = self.at(cause, param_env).eq(b, a)?;
-            self.leak_check(false, cause_span, &skol_map, snapshot)?;
-            self.pop_skolemized(param_env, skol_map, snapshot);
             Ok(eqty_ok.unit())
         })
     }
@@ -911,14 +908,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             return None;
         }
 
-        Some(self.commit_if_ok(|snapshot| {
-            let (ty::SubtypePredicate { a_is_expected, a, b}, param_env, skol_map) =
+        Some(self.commit_if_ok(|_snapshot| {
+            let (ty::SubtypePredicate { a_is_expected, a, b}, param_env, _skol_map) =
                 self.skolemize_late_bound_regions(param_env, predicate);
 
-            let cause_span = cause.span;
             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(param_env, skol_map, snapshot);
             Ok(ok.unit())
         }))
     }
@@ -929,16 +923,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                      predicate: &ty::PolyRegionOutlivesPredicate<'tcx>)
                                      -> UnitResult<'tcx>
     {
-        self.commit_if_ok(|snapshot| {
-            let (ty::OutlivesPredicate(r_a, r_b), param_env, skol_map) =
-                self.skolemize_late_bound_regions(param_env, predicate);
-            let origin =
-                SubregionOrigin::from_obligation_cause(cause,
-                                                       || RelateRegionParamBound(cause.span));
-            self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
-            self.leak_check(false, cause.span, &skol_map, snapshot)?;
-            Ok(self.pop_skolemized(param_env, skol_map, snapshot))
-        })
+        let (ty::OutlivesPredicate(r_a, r_b), _param_env, _skol_map) =
+            self.skolemize_late_bound_regions(param_env, predicate);
+        let origin =
+            SubregionOrigin::from_obligation_cause(cause,
+                                                   || RelateRegionParamBound(cause.span));
+        self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
+        Ok(())
     }
 
     pub fn next_ty_var_id(&self,
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index fc45485cec40b..96b908da4be95 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -134,25 +134,12 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
            obligation);
 
     let infcx = selcx.infcx();
-    infcx.commit_if_ok(|snapshot| {
-        let (skol_predicate, param_env, skol_map) =
+    infcx.commit_if_ok(|_snapshot| {
+        let (skol_predicate, param_env, _skol_map) =
             infcx.skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
 
         let skol_obligation = obligation.with(skol_predicate).with_env(param_env);
-        let r = match project_and_unify_type(selcx, &skol_obligation) {
-            Ok(result) => {
-                let span = obligation.cause.span;
-                match infcx.leak_check(false, span, &skol_map, snapshot) {
-                    Ok(()) => Ok(infcx.plug_leaks(param_env, skol_map, snapshot, result)),
-                    Err(e) => Err(MismatchedProjectionTypes { err: e }),
-                }
-            }
-            Err(e) => {
-                Err(e)
-            }
-        };
-
-        r
+        project_and_unify_type(selcx, &skol_obligation)
     })
 }
 
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index dad871dba2ce8..57c1a91f7c34a 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -1394,9 +1394,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             _ => return
         }
 
-        let result = self.probe(|this, snapshot| {
-            this.match_projection_obligation_against_definition_bounds(obligation,
-                                                                       snapshot)
+        let result = self.probe(|this, _snapshot| {
+            this.match_projection_obligation_against_definition_bounds(obligation)
         });
 
         if result {
@@ -1406,18 +1405,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn match_projection_obligation_against_definition_bounds(
         &mut self,
-        obligation: &TraitObligation<'tcx>,
-        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
+        obligation: &TraitObligation<'tcx>)
         -> bool
     {
         let poly_trait_predicate =
             self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
-        let (skol_trait_predicate, param_env, skol_map) =
+        let (skol_trait_predicate, param_env, _skol_map) =
             self.infcx().skolemize_late_bound_regions(obligation.param_env, &poly_trait_predicate);
         debug!("match_projection_obligation_against_definition_bounds: \
-                skol_trait_predicate={:?} skol_map={:?}",
-               skol_trait_predicate,
-               skol_map);
+                skol_trait_predicate={:?}",
+               skol_trait_predicate);
 
         let obligation = obligation.clone().with_env(param_env);
 
@@ -1450,9 +1447,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 |bound| self.probe(
                     |this, _| this.match_projection(&obligation,
                                                     bound.clone(),
-                                                    skol_trait_predicate.trait_ref.clone(),
-                                                    &skol_map,
-                                                    snapshot)));
+                                                    skol_trait_predicate.trait_ref.clone())));
 
         debug!("match_projection_obligation_against_definition_bounds: \
                 matching_bound={:?}",
@@ -1463,12 +1458,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 // Repeat the successful match, if any, this time outside of a probe.
                 let result = self.match_projection(&obligation,
                                                    bound,
-                                                   skol_trait_predicate.trait_ref.clone(),
-                                                   &skol_map,
-                                                   snapshot);
-
-                self.infcx.pop_skolemized(param_env, skol_map, snapshot);
-
+                                                   skol_trait_predicate.trait_ref.clone());
                 assert!(result);
                 true
             }
@@ -1478,9 +1468,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn match_projection(&mut self,
                         obligation: &TraitObligation<'tcx>,
                         trait_bound: ty::PolyTraitRef<'tcx>,
-                        skol_trait_ref: ty::TraitRef<'tcx>,
-                        skol_map: &infer::SkolemizationMap<'tcx>,
-                        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
+                        skol_trait_ref: ty::TraitRef<'tcx>)
                         -> bool
     {
         assert!(!skol_trait_ref.has_escaping_regions());
@@ -1488,11 +1476,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         .sup(ty::Binder(skol_trait_ref), trait_bound) {
             Ok(InferOk { obligations, .. }) => {
                 self.inferred_obligations.extend(obligations);
+                true
             }
-            Err(_) => { return false; }
+            Err(_) => false,
         }
-
-        self.infcx.leak_check(false, obligation.cause.span, skol_map, snapshot).is_ok()
     }
 
     /// Given an obligation like `<SomeTrait for T>`, search the obligations that the caller
@@ -1670,8 +1657,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             obligation.predicate.def_id(),
             obligation.predicate.0.trait_ref.self_ty(),
             |impl_def_id| {
-                self.probe(|this, snapshot| { /* [1] */
-                    match this.match_impl(impl_def_id, obligation, snapshot) {
+                self.probe(|this, _snapshot| { /* [1] */
+                    match this.match_impl(impl_def_id, obligation) {
                         Ok(skol_map) => {
                             candidates.vec.push(ImplCandidate(impl_def_id));
 
@@ -2235,8 +2222,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         types.skip_binder().into_iter().flat_map(|ty| { // binder moved -\
             let ty: ty::Binder<Ty<'tcx>> = ty::Binder(ty); // <----------/
 
-            self.in_snapshot(|this, snapshot| {
-                let (skol_ty, param_env, skol_map) =
+            self.in_snapshot(|this, _snapshot| {
+                let (skol_ty, param_env, _skol_map) =
                     this.infcx().skolemize_late_bound_regions(param_env, &ty);
                 let Normalized { value: normalized_ty, mut obligations } =
                     project::normalize_with_depth(this,
@@ -2252,7 +2239,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                                        normalized_ty,
                                                        &[]);
                 obligations.push(skol_obligation);
-                this.infcx().plug_leaks(param_env, skol_map, snapshot, obligations)
+                obligations
             })
         }).collect()
     }
@@ -2338,10 +2325,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn confirm_projection_candidate(&mut self,
                                     obligation: &TraitObligation<'tcx>)
     {
-        self.in_snapshot(|this, snapshot| {
+        self.in_snapshot(|this, _snapshot| {
             let result =
-                this.match_projection_obligation_against_definition_bounds(obligation,
-                                                                           snapshot);
+                this.match_projection_obligation_against_definition_bounds(obligation);
             assert!(result);
         })
     }
@@ -2451,18 +2437,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             trait_def_id,
             nested);
 
-        let trait_obligations = self.in_snapshot(|this, snapshot| {
+        let trait_obligations = self.in_snapshot(|this, _snapshot| {
             let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
-            let (trait_ref, param_env, skol_map) =
+            let (trait_ref, param_env, _skol_map) =
                 this.infcx().skolemize_late_bound_regions(obligation.param_env, &poly_trait_ref);
             let cause = obligation.derived_cause(ImplDerivedObligation);
             this.impl_or_trait_obligations(cause,
                                            obligation.recursion_depth + 1,
                                            param_env,
                                            trait_def_id,
-                                           &trait_ref.substs,
-                                           skol_map,
-                                           snapshot)
+                                           &trait_ref.substs)
         });
 
         obligations.extend(trait_obligations);
@@ -2486,19 +2470,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         // First, create the substitutions by matching the impl again,
         // this time not in a probe.
-        self.in_snapshot(|this, snapshot| {
-            let (substs, param_env, skol_map) =
-                this.rematch_impl(impl_def_id, obligation,
-                                  snapshot);
+        self.in_snapshot(|this, _snapshot| {
+            let (substs, param_env, _skol_map) =
+                this.rematch_impl(impl_def_id, obligation);
             debug!("confirm_impl_candidate substs={:?}", substs);
             let cause = obligation.derived_cause(ImplDerivedObligation);
             this.vtable_impl(impl_def_id,
                              substs,
                              cause,
                              obligation.recursion_depth + 1,
-                             param_env,
-                             skol_map,
-                             snapshot)
+                             param_env)
         })
     }
 
@@ -2507,25 +2488,20 @@ 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<'cx, 'tcx>)
+                   param_env: ty::ParamEnv<'tcx>)
                    -> VtableImplData<'tcx, PredicateObligation<'tcx>>
     {
-        debug!("vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={}, skol_map={:?})",
+        debug!("vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={})",
                impl_def_id,
                substs,
-               recursion_depth,
-               skol_map);
+               recursion_depth);
 
         let mut impl_obligations =
             self.impl_or_trait_obligations(cause,
                                            recursion_depth,
                                            param_env,
                                            impl_def_id,
-                                           &substs.value,
-                                           skol_map,
-                                           snapshot);
+                                           &substs.value);
 
         debug!("vtable_impl: impl_def_id={:?} impl_obligations={:?}",
                impl_def_id,
@@ -2997,13 +2973,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn rematch_impl(&mut self,
                     impl_def_id: DefId,
-                    obligation: &TraitObligation<'tcx>,
-                    snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
+                    obligation: &TraitObligation<'tcx>)
                     -> (Normalized<'tcx, &'tcx Substs<'tcx>>,
                         ty::ParamEnv<'tcx>,
                         infer::SkolemizationMap<'tcx>)
     {
-        match self.match_impl(impl_def_id, obligation, snapshot) {
+        match self.match_impl(impl_def_id, obligation) {
             Ok(tuple) => tuple,
             Err(()) => {
                 bug!("Impl {:?} was matchable against {:?} but now is not",
@@ -3015,8 +2990,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn match_impl(&mut self,
                   impl_def_id: DefId,
-                  obligation: &TraitObligation<'tcx>,
-                  snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
+                  obligation: &TraitObligation<'tcx>)
                   -> Result<(Normalized<'tcx, &'tcx Substs<'tcx>>,
                              ty::ParamEnv<'tcx>,
                              infer::SkolemizationMap<'tcx>), ()>
@@ -3064,14 +3038,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                       })?;
         self.inferred_obligations.extend(obligations);
 
-        if let Err(e) = self.infcx.leak_check(false,
-                                              obligation.cause.span,
-                                              &skol_map,
-                                              snapshot) {
-            debug!("match_impl: failed leak check due to `{}`", e);
-            return Err(());
-        }
-
         debug!("match_impl: success impl_substs={:?}", impl_substs);
         Ok((Normalized {
             value: impl_substs,
@@ -3214,9 +3180,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, '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>,
-                                 snapshot: &infer::CombinedSnapshot<'cx, 'tcx>)
+                                 substs: &Substs<'tcx>) // for impl or trait
                                  -> Vec<PredicateObligation<'tcx>>
     {
         debug!("impl_or_trait_obligations(def_id={:?})", def_id);
@@ -3238,7 +3202,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // that order.
         let predicates = tcx.predicates_of(def_id);
         assert_eq!(predicates.parent, None);
-        let predicates = predicates.predicates.iter().flat_map(|predicate| {
+        predicates.predicates.iter().flat_map(|predicate| {
             let predicate = normalize_with_depth(self, param_env, cause.clone(), recursion_depth,
                                                  &predicate.subst(tcx, substs));
             predicate.obligations.into_iter().chain(
@@ -3248,8 +3212,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     param_env,
                     predicate: predicate.value
                 }))
-        }).collect();
-        self.infcx().plug_leaks(param_env, skol_map, snapshot, predicates)
+        }).collect()
     }
 }
 
diff --git a/src/test/compile-fail/associated-types-eq-hr.rs b/src/test/compile-fail/associated-types-eq-hr.rs
index 52a2ca9082d23..ea2c2a23223ee 100644
--- a/src/test/compile-fail/associated-types-eq-hr.rs
+++ b/src/test/compile-fail/associated-types-eq-hr.rs
@@ -87,23 +87,33 @@ fn tuple_four<T>()
     // not ok for tuple, two lifetimes, and lifetime matching is invariant
 }
 
-pub fn main() {
+fn a() {
     foo::<IntStruct>();
     foo::<UintStruct>(); //~ ERROR type mismatch
+}
 
+fn b() {
     bar::<IntStruct>(); //~ ERROR type mismatch
     bar::<UintStruct>();
+}
 
+fn c() {
     tuple_one::<Tuple>();
-    //~^ ERROR E0277
-    //~| ERROR type mismatch
+    //~^ ERROR type mismatch
+}
 
+fn d() {
     tuple_two::<Tuple>();
-    //~^ ERROR E0277
-    //~| ERROR type mismatch
+    //~^ ERROR type mismatch
+}
 
+fn e() {
     tuple_three::<Tuple>();
+}
 
+fn g() {
     tuple_four::<Tuple>();
     //~^ ERROR E0277
 }
+
+fn main() { }
diff --git a/src/test/run-pass/coherence-subtyping.rs b/src/test/compile-fail/coherence-different-binding.rs
similarity index 79%
rename from src/test/run-pass/coherence-subtyping.rs
rename to src/test/compile-fail/coherence-different-binding.rs
index eb04514271c75..4ff47be75fd7a 100644
--- a/src/test/run-pass/coherence-subtyping.rs
+++ b/src/test/compile-fail/coherence-different-binding.rs
@@ -18,7 +18,7 @@ trait Contravariant {
 impl Contravariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
 }
 
-impl Contravariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
+impl Contravariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { //~ ERROR conflicting implementations
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -30,7 +30,7 @@ trait Covariant {
 impl Covariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
 }
 
-impl Covariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
+impl Covariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { //~ ERROR conflicting implementations
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -42,7 +42,7 @@ trait Invariant {
 impl Invariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
 }
 
-impl Invariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
+impl Invariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { //~ ERROR conflicting implementations
 }
 
 fn main() { }

From 5936311b250b47faf5104e4010b4cbc86b1ca162 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 28 Aug 2017 04:30:27 -0400
Subject: [PATCH 24/63] kill `pop_skolemized()` in `region_inference`

---
 src/librustc/infer/region_inference/mod.rs | 85 ----------------------
 1 file changed, 85 deletions(-)

diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index fc6b073d01f13..70d0cdca652f6 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -31,7 +31,6 @@ use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
 use std::cell::{Cell, RefCell};
 use std::cmp;
 use std::fmt;
-use std::mem;
 use std::u32;
 
 mod graphviz;
@@ -485,90 +484,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         self.var_infos.borrow()[vid.index as usize].origin.clone()
     }
 
-    /// Removes all the edges to/from the skolemized regions that are
-    /// in `skols`. This is used after a higher-ranked operation
-    /// completes to remove all trace of the skolemized regions
-    /// created in that time.
-    pub fn pop_skolemized(&self,
-                          param_env: ty::ParamEnv<'tcx>,
-                          skols: &FxHashSet<ty::Region<'tcx>>,
-                          snapshot: &RegionSnapshot) {
-        debug!("pop_skolemized_regions(param_env.universe={:?}, skols={:?})",
-               param_env.universe,
-               skols);
-
-        assert!(self.in_snapshot());
-        assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot);
-        assert!(param_env.universe.as_usize() >= skols.len(),
-                "popping more skolemized variables than actually exist, \
-                 universe now = {:?}, skols.len = {}",
-                param_env.universe,
-                skols.len());
-
-        let last_to_pop = param_env.universe.subuniverse();
-        let first_to_pop = ty::UniverseIndex::from(last_to_pop.as_u32() - (skols.len() as u32));
-
-        debug_assert! {
-            skols.iter()
-                 .all(|&k| match *k {
-                     ty::ReSkolemized(universe, _) =>
-                         universe >= first_to_pop &&
-                         universe < last_to_pop,
-                     _ =>
-                         false
-                 }),
-            "invalid skolemization keys or keys out of range ({:?}..{:?}): {:?}",
-            first_to_pop,
-            last_to_pop,
-            skols
-        }
-
-        let mut undo_log = self.undo_log.borrow_mut();
-
-        let constraints_to_kill: Vec<usize> =
-            undo_log.iter()
-                    .enumerate()
-                    .rev()
-                    .filter(|&(_, undo_entry)| kill_constraint(skols, undo_entry))
-                    .map(|(index, _)| index)
-                    .collect();
-
-        for index in constraints_to_kill {
-            let undo_entry = mem::replace(&mut undo_log[index], Purged);
-            self.rollback_undo_entry(undo_entry);
-        }
-
-        return;
-
-        fn kill_constraint<'tcx>(skols: &FxHashSet<ty::Region<'tcx>>,
-                                 undo_entry: &UndoLogEntry<'tcx>)
-                                 -> bool {
-            match undo_entry {
-                &AddConstraint(ConstrainVarSubVar(..)) =>
-                    false,
-                &AddConstraint(ConstrainRegSubVar(a, _)) =>
-                    skols.contains(&a),
-                &AddConstraint(ConstrainVarSubReg(_, b)) =>
-                    skols.contains(&b),
-                &AddConstraint(ConstrainRegSubReg(a, b)) =>
-                    skols.contains(&a) || skols.contains(&b),
-                &AddGiven(..) =>
-                    false,
-                &AddVerify(_) =>
-                    false,
-                &AddCombination(_, ref two_regions) =>
-                    skols.contains(&two_regions.a) ||
-                    skols.contains(&two_regions.b),
-                &AddVar(..) |
-                &OpenSnapshot |
-                &Purged |
-                &CommitedSnapshot =>
-                    false,
-            }
-        }
-
-    }
-
     pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region<'tcx> {
         // Creates a fresh bound variable for use in GLB computations.
         // See discussion of GLB computation in the large comment at

From c24236af08ae5f3182b964fb41c343a04fa1caf6 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 28 Aug 2017 05:27:25 -0400
Subject: [PATCH 25/63] track the `param_env` when making subregion constraints

This will allow us to know what universe we were in and also what
bounds were in scope.
---
 src/librustc/infer/equate.rs               |  2 +-
 src/librustc/infer/glb.rs                  |  2 +-
 src/librustc/infer/lub.rs                  |  2 +-
 src/librustc/infer/mod.rs                  |  5 +-
 src/librustc/infer/region_inference/mod.rs | 12 ++--
 src/librustc/infer/sub.rs                  |  2 +-
 src/librustc_typeck/check/dropck.rs        |  6 +-
 src/librustc_typeck/check/regionck.rs      | 68 ++++++++++++++--------
 src/librustc_typeck/coherence/builtin.rs   |  2 +-
 9 files changed, 64 insertions(+), 37 deletions(-)

diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs
index b2e3318b65761..86cb68a04c0c9 100644
--- a/src/librustc/infer/equate.rs
+++ b/src/librustc/infer/equate.rs
@@ -115,7 +115,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                a,
                b);
         let origin = Subtype(self.fields.trace.clone());
-        self.fields.infcx.region_vars.make_eqregion(origin, a, b);
+        self.fields.infcx.region_vars.make_eqregion(origin, self.param_env, a, b);
         Ok(a)
     }
 
diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs
index f8c364a730714..78240f25e84d6 100644
--- a/src/librustc/infer/glb.rs
+++ b/src/librustc/infer/glb.rs
@@ -70,7 +70,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                b);
 
         let origin = Subtype(self.fields.trace.clone());
-        Ok(self.fields.infcx.region_vars.glb_regions(origin, a, b))
+        Ok(self.fields.infcx.region_vars.glb_regions(origin, self.param_env, a, b))
     }
 
     fn binders<T>(&mut self, a: &ty::Binder<T>, b: &ty::Binder<T>)
diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs
index b1a75f48007f0..c017d52a49d35 100644
--- a/src/librustc/infer/lub.rs
+++ b/src/librustc/infer/lub.rs
@@ -70,7 +70,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                b);
 
         let origin = Subtype(self.fields.trace.clone());
-        Ok(self.fields.infcx.region_vars.lub_regions(origin, a, b))
+        Ok(self.fields.infcx.region_vars.lub_regions(origin, self.param_env, a, b))
     }
 
     fn binders<T>(&mut self, a: &ty::Binder<T>, b: &ty::Binder<T>)
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 035fcdad9410e..c310b9f22b7ab 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -862,10 +862,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
     pub fn sub_regions(&self,
                        origin: SubregionOrigin<'tcx>,
+                       param_env: ty::ParamEnv<'tcx>,
                        a: ty::Region<'tcx>,
                        b: ty::Region<'tcx>) {
         debug!("sub_regions({:?} <: {:?})", a, b);
-        self.region_vars.make_subregion(origin, a, b);
+        self.region_vars.make_subregion(origin, param_env, a, b);
     }
 
     pub fn equality_predicate(&self,
@@ -928,7 +929,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         let origin =
             SubregionOrigin::from_obligation_cause(cause,
                                                    || RelateRegionParamBound(cause.span));
-        self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
+        self.sub_regions(origin, param_env, r_b, r_a); // `b : a` ==> `a <= b`
         Ok(())
     }
 
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 70d0cdca652f6..93050aa744c74 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -564,13 +564,14 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
     pub fn make_eqregion(&self,
                          origin: SubregionOrigin<'tcx>,
+                         param_env: ty::ParamEnv<'tcx>,
                          sub: Region<'tcx>,
                          sup: Region<'tcx>) {
         if sub != sup {
             // Eventually, it would be nice to add direct support for
             // equating regions.
-            self.make_subregion(origin.clone(), sub, sup);
-            self.make_subregion(origin, sup, sub);
+            self.make_subregion(origin.clone(), param_env, sub, sup);
+            self.make_subregion(origin, param_env, sup, sub);
 
             if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) {
                 self.unification_table.borrow_mut().union(sub, sup);
@@ -580,6 +581,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
     pub fn make_subregion(&self,
                           origin: SubregionOrigin<'tcx>,
+                          _param_env: ty::ParamEnv<'tcx>,
                           sub: Region<'tcx>,
                           sup: Region<'tcx>) {
         // cannot add constraints once regions are resolved
@@ -632,6 +634,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
     pub fn lub_regions(&self,
                        origin: SubregionOrigin<'tcx>,
+                       param_env: ty::ParamEnv<'tcx>,
                        a: Region<'tcx>,
                        b: Region<'tcx>)
                        -> Region<'tcx> {
@@ -650,7 +653,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
             _ => {
                 self.combine_vars(Lub, a, b, origin.clone(), |this, old_r, new_r| {
-                    this.make_subregion(origin.clone(), old_r, new_r)
+                    this.make_subregion(origin.clone(), param_env, old_r, new_r)
                 })
             }
         }
@@ -658,6 +661,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
     pub fn glb_regions(&self,
                        origin: SubregionOrigin<'tcx>,
+                       param_env: ty::ParamEnv<'tcx>,
                        a: Region<'tcx>,
                        b: Region<'tcx>)
                        -> Region<'tcx> {
@@ -676,7 +680,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
             _ => {
                 self.combine_vars(Glb, a, b, origin.clone(), |this, old_r, new_r| {
-                    this.make_subregion(origin.clone(), new_r, old_r)
+                    this.make_subregion(origin.clone(), param_env, new_r, old_r)
                 })
             }
         }
diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs
index bf36af86464ac..2ac386ffec68f 100644
--- a/src/librustc/infer/sub.rs
+++ b/src/librustc/infer/sub.rs
@@ -147,7 +147,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
         // from the "cause" field, we could perhaps give more tailored
         // error messages.
         let origin = SubregionOrigin::Subtype(self.fields.trace.clone());
-        self.fields.infcx.region_vars.make_subregion(origin, a, b);
+        self.fields.infcx.region_vars.make_subregion(origin, self.param_env, a, b);
 
         Ok(a)
     }
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 0a5f3c76aec98..b9d978b69c789 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -307,7 +307,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
                 // A projection that we couldn't resolve - it
                 // might have a destructor.
                 ty::TyProjection(..) | ty::TyAnon(..) => {
-                    rcx.type_must_outlive(origin(), ty, parent_scope);
+                    rcx.type_must_outlive(origin(), rcx.fcx.param_env, ty, parent_scope);
                 }
 
                 _ => {
@@ -320,9 +320,9 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
 
         for outlive in outlives {
             if let Some(r) = outlive.as_region() {
-                rcx.sub_regions(origin(), parent_scope, r);
+                rcx.sub_regions(origin(), rcx.fcx.param_env, parent_scope, r);
             } else if let Some(ty) = outlive.as_type() {
-                rcx.type_must_outlive(origin(), ty, parent_scope);
+                rcx.type_must_outlive(origin(), rcx.fcx.param_env, ty, parent_scope);
             }
         }
     }
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index f2d9e77975c99..155173b641700 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -371,7 +371,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
                    r_o, r_o.cause);
             let sup_type = self.resolve_type(r_o.sup_type);
             let origin = self.code_to_origin(&r_o.cause, sup_type);
-            self.type_must_outlive(origin, sup_type, r_o.sub_region);
+            self.type_must_outlive(origin, self.fcx.param_env, sup_type, r_o.sub_region);
         }
 
         // Processing the region obligations should not cause the list to grow further:
@@ -672,7 +672,9 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
         let expr_region = self.tcx.mk_region(ty::ReScope(
             region::Scope::Node(expr.hir_id.local_id)));
         self.type_must_outlive(infer::ExprTypeIsNotInScope(expr_ty, expr.span),
-                               expr_ty, expr_region);
+                               self.fcx.param_env,
+                               expr_ty,
+                               expr_region);
 
         let is_method_call = self.tables.borrow().is_method_call(expr);
 
@@ -763,7 +765,9 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
                 let rhs_ty = self.resolve_expr_type_adjusted(&rhs);
                 for &ty in &[lhs_ty, rhs_ty] {
                     self.type_must_outlive(infer::Operand(expr.span),
-                                           ty, expr_region);
+                                           self.fcx.param_env,
+                                           ty,
+                                           expr_region);
                 }
                 intravisit::walk_expr(self, expr);
             }
@@ -818,7 +822,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
                 // FIXME(https://github.com/rust-lang/rfcs/issues/811)
                 // nested method calls requires that this rule change
                 let ty0 = self.resolve_node_type(expr.hir_id);
-                self.type_must_outlive(infer::AddrOf(expr.span), ty0, expr_region);
+                self.type_must_outlive(infer::AddrOf(expr.span),
+                                       self.fcx.param_env,
+                                       ty0,
+                                       expr_region);
                 intravisit::walk_expr(self, expr);
             }
 
@@ -892,7 +899,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             /*From:*/ (&ty::TyRef(from_r, ref from_mt),
             /*To:  */  &ty::TyRef(to_r, ref to_mt)) => {
                 // Target cannot outlive source, naturally.
-                self.sub_regions(infer::Reborrow(cast_expr.span), to_r, from_r);
+                self.sub_regions(infer::Reborrow(cast_expr.span), self.fcx.param_env, to_r, from_r);
                 self.walk_cast(cast_expr, from_mt.ty, to_mt.ty);
             }
 
@@ -900,7 +907,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             /*To:  */  &ty::TyDynamic(.., r)) => {
                 // When T is existentially quantified as a trait
                 // `Foo+'to`, it must outlive the region bound `'to`.
-                self.type_must_outlive(infer::RelateObjectBound(cast_expr.span), from_ty, r);
+                self.type_must_outlive(infer::RelateObjectBound(cast_expr.span),
+                                       self.fcx.param_env,
+                                       from_ty,
+                                       r);
             }
 
             /*From:*/ (&ty::TyAdt(from_def, _),
@@ -1030,9 +1040,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
 
                 // Specialized version of constrain_call.
                 self.type_must_outlive(infer::CallRcvr(expr.span),
-                                       input, expr_region);
+                                       self.fcx.param_env,
+                                       input,
+                                       expr_region);
                 self.type_must_outlive(infer::CallReturn(expr.span),
-                                       output, expr_region);
+                                       self.fcx.param_env,
+                                       output,
+                                       expr_region);
             }
 
             if let adjustment::Adjust::Borrow(ref autoref) = adjustment.kind {
@@ -1063,7 +1077,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
                                            minimum_lifetime: ty::Region<'tcx>,
                                            maximum_lifetime: ty::Region<'tcx>) {
         self.sub_regions(infer::DerefPointer(deref_span),
-                         minimum_lifetime, maximum_lifetime)
+                         self.fcx.param_env,
+                         minimum_lifetime,
+                         maximum_lifetime)
     }
 
     fn check_safety_of_rvalue_destructor_if_necessary(&mut self,
@@ -1104,7 +1120,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             match mt.ty.sty {
                 ty::TySlice(_) | ty::TyStr => {
                     self.sub_regions(infer::IndexSlice(index_expr.span),
-                                     self.tcx.mk_region(r_index_expr), r_ptr);
+                                     self.fcx.param_env,
+                                     self.tcx.mk_region(r_index_expr),
+                                     r_ptr);
                 }
                 _ => {}
             }
@@ -1134,7 +1152,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
                 ty={}, ty0={}, id={:?}, minimum_lifetime={:?})",
                 ty,  ty0,
                 hir_id, minimum_lifetime);
-        self.type_must_outlive(origin, ty, minimum_lifetime);
+        self.type_must_outlive(origin, self.fcx.param_env, ty, minimum_lifetime);
     }
 
     /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the
@@ -1273,7 +1291,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         let mut borrow_kind = borrow_kind;
 
         let origin = infer::DataBorrowed(borrow_cmt.ty, span);
-        self.type_must_outlive(origin, borrow_cmt.ty, borrow_region);
+        self.type_must_outlive(origin, self.fcx.param_env, borrow_cmt.ty, borrow_region);
 
         loop {
             debug!("link_region(borrow_region={:?}, borrow_kind={:?}, borrow_cmt={:?})",
@@ -1402,7 +1420,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         debug!("link_reborrowed_region: {:?} <= {:?}",
                borrow_region,
                ref_region);
-        self.sub_regions(cause, borrow_region, ref_region);
+        self.sub_regions(cause, self.fcx.param_env, borrow_region, ref_region);
 
         // If we end up needing to recurse and establish a region link
         // with `ref_cmt`, calculate what borrow kind we will end up
@@ -1484,12 +1502,12 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         let origin = infer::ParameterInScope(origin, expr_span);
 
         for region in substs.regions() {
-            self.sub_regions(origin.clone(), expr_region, region);
+            self.sub_regions(origin.clone(), self.fcx.param_env, expr_region, region);
         }
 
         for ty in substs.types() {
             let ty = self.resolve_type(ty);
-            self.type_must_outlive(origin.clone(), ty, expr_region);
+            self.type_must_outlive(origin.clone(), self.fcx.param_env, ty, expr_region);
         }
     }
 
@@ -1498,6 +1516,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
     /// `region`.
     pub fn type_must_outlive(&self,
                              origin: infer::SubregionOrigin<'tcx>,
+                             param_env: ty::ParamEnv<'tcx>,
                              ty: Ty<'tcx>,
                              region: ty::Region<'tcx>)
     {
@@ -1511,11 +1530,12 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         assert!(!ty.has_escaping_regions());
 
         let components = self.tcx.outlives_components(ty);
-        self.components_must_outlive(origin, components, region);
+        self.components_must_outlive(origin, param_env, components, region);
     }
 
     fn components_must_outlive(&self,
                                origin: infer::SubregionOrigin<'tcx>,
+                               param_env: ty::ParamEnv<'tcx>,
                                components: Vec<Component<'tcx>>,
                                region: ty::Region<'tcx>)
     {
@@ -1523,16 +1543,16 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             let origin = origin.clone();
             match component {
                 Component::Region(region1) => {
-                    self.sub_regions(origin, region, region1);
+                    self.sub_regions(origin, param_env, region, region1);
                 }
                 Component::Param(param_ty) => {
-                    self.param_ty_must_outlive(origin, region, param_ty);
+                    self.param_ty_must_outlive(origin, param_env, region, param_ty);
                 }
                 Component::Projection(projection_ty) => {
-                    self.projection_must_outlive(origin, region, projection_ty);
+                    self.projection_must_outlive(origin, param_env, region, projection_ty);
                 }
                 Component::EscapingProjection(subcomponents) => {
-                    self.components_must_outlive(origin, subcomponents, region);
+                    self.components_must_outlive(origin, param_env, subcomponents, region);
                 }
                 Component::UnresolvedInferenceVariable(v) => {
                     // ignore this, we presume it will yield an error
@@ -1548,6 +1568,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
 
     fn param_ty_must_outlive(&self,
                              origin: infer::SubregionOrigin<'tcx>,
+                             _param_env: ty::ParamEnv<'tcx>,
                              region: ty::Region<'tcx>,
                              param_ty: ty::ParamTy) {
         debug!("param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})",
@@ -1560,6 +1581,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
 
     fn projection_must_outlive(&self,
                                origin: infer::SubregionOrigin<'tcx>,
+                               param_env: ty::ParamEnv<'tcx>,
                                region: ty::Region<'tcx>,
                                projection_ty: ty::ProjectionTy<'tcx>)
     {
@@ -1614,11 +1636,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             debug!("projection_must_outlive: no declared bounds");
 
             for component_ty in projection_ty.substs.types() {
-                self.type_must_outlive(origin.clone(), component_ty, region);
+                self.type_must_outlive(origin.clone(), param_env, component_ty, region);
             }
 
             for r in projection_ty.substs.regions() {
-                self.sub_regions(origin.clone(), region, r);
+                self.sub_regions(origin.clone(), param_env, region, r);
             }
 
             return;
@@ -1637,7 +1659,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             debug!("projection_must_outlive: unique declared bound = {:?}", unique_bound);
             if projection_ty.substs.regions().any(|r| env_bounds.contains(&r)) {
                 debug!("projection_must_outlive: unique declared bound appears in trait ref");
-                self.sub_regions(origin.clone(), region, unique_bound);
+                self.sub_regions(origin.clone(), param_env, region, unique_bound);
                 return;
             }
         }
diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs
index fedfa51d61d11..30a23244cf000 100644
--- a/src/librustc_typeck/coherence/builtin.rs
+++ b/src/librustc_typeck/coherence/builtin.rs
@@ -224,7 +224,7 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         };
         let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
             (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
-                infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
+                infcx.sub_regions(infer::RelateObjectBound(span), param_env, r_b, r_a);
                 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
             }
 

From cfbc5364ed2d7a9871ec2f7a4c53d8d08dabc49d Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 28 Aug 2017 05:50:47 -0400
Subject: [PATCH 26/63] region_inference: make some things private

---
 src/librustc/infer/region_inference/mod.rs | 50 +++++++++-------------
 1 file changed, 20 insertions(+), 30 deletions(-)

diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 93050aa744c74..f57ddc57b96e7 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -11,8 +11,7 @@
 //! See README.md
 
 pub use self::Constraint::*;
-pub use self::UndoLogEntry::*;
-pub use self::CombineMapType::*;
+use self::UndoLogEntry::*;
 pub use self::RegionResolutionError::*;
 pub use self::VarValue::*;
 
@@ -101,13 +100,13 @@ pub enum VerifyBound<'tcx> {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct TwoRegions<'tcx> {
+struct TwoRegions<'tcx> {
     a: Region<'tcx>,
     b: Region<'tcx>,
 }
 
 #[derive(Copy, Clone, PartialEq)]
-pub enum UndoLogEntry<'tcx> {
+enum UndoLogEntry<'tcx> {
     /// Pushed when we start a snapshot.
     OpenSnapshot,
 
@@ -130,18 +129,10 @@ pub enum UndoLogEntry<'tcx> {
 
     /// We added a GLB/LUB "combination variable"
     AddCombination(CombineMapType, TwoRegions<'tcx>),
-
-    /// During skolemization, we sometimes purge entries from the undo
-    /// log in a kind of minisnapshot (unlike other snapshots, this
-    /// purging actually takes place *on success*). In that case, we
-    /// replace the corresponding entry with `Noop` so as to avoid the
-    /// need to do a bunch of swapping. (We can't use `swap_remove` as
-    /// the order of the vector is important.)
-    Purged,
 }
 
 #[derive(Copy, Clone, PartialEq)]
-pub enum CombineMapType {
+enum CombineMapType {
     Lub,
     Glb,
 }
@@ -177,7 +168,7 @@ pub enum ProcessedErrorOrigin<'tcx> {
     VariableFailure(RegionVariableOrigin),
 }
 
-pub type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
+type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
 
 #[derive(Clone, Debug)]
 struct RegionVariableInfo {
@@ -320,7 +311,6 @@ impl<'a, 'gcx, 'tcx> TaintSet<'tcx> {
                             self.add_edge(verifys[i].region, b);
                         });
                     }
-                    &Purged |
                     &AddCombination(..) |
                     &AddVar(..) |
                     &OpenSnapshot |
@@ -414,12 +404,12 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             .rollback_to(snapshot.region_snapshot);
     }
 
-    pub fn rollback_undo_entry(&self, undo_entry: UndoLogEntry<'tcx>) {
+    fn rollback_undo_entry(&self, undo_entry: UndoLogEntry<'tcx>) {
         match undo_entry {
             OpenSnapshot => {
                 panic!("Failure to observe stack discipline");
             }
-            Purged | CommitedSnapshot => {
+            CommitedSnapshot => {
                 // nothing to do here
             }
             AddVar(vid) => {
@@ -437,10 +427,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             AddGiven(sub, sup) => {
                 self.givens.borrow_mut().remove(&(sub, sup));
             }
-            AddCombination(Glb, ref regions) => {
+            AddCombination(CombineMapType::Glb, ref regions) => {
                 self.glbs.borrow_mut().remove(regions);
             }
-            AddCombination(Lub, ref regions) => {
+            AddCombination(CombineMapType::Lub, ref regions) => {
                 self.lubs.borrow_mut().remove(regions);
             }
         }
@@ -652,7 +642,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             }
 
             _ => {
-                self.combine_vars(Lub, a, b, origin.clone(), |this, old_r, new_r| {
+                self.combine_vars(CombineMapType::Lub, a, b, origin.clone(), |this, old_r, new_r| {
                     this.make_subregion(origin.clone(), param_env, old_r, new_r)
                 })
             }
@@ -679,7 +669,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             }
 
             _ => {
-                self.combine_vars(Glb, a, b, origin.clone(), |this, old_r, new_r| {
+                self.combine_vars(CombineMapType::Glb, a, b, origin.clone(), |this, old_r, new_r| {
                     this.make_subregion(origin.clone(), param_env, new_r, old_r)
                 })
             }
@@ -708,18 +698,18 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
     fn combine_map(&self, t: CombineMapType) -> &RefCell<CombineMap<'tcx>> {
         match t {
-            Glb => &self.glbs,
-            Lub => &self.lubs,
+            CombineMapType::Glb => &self.glbs,
+            CombineMapType::Lub => &self.lubs,
         }
     }
 
-    pub fn combine_vars<F>(&self,
-                           t: CombineMapType,
-                           a: Region<'tcx>,
-                           b: Region<'tcx>,
-                           origin: SubregionOrigin<'tcx>,
-                           mut relate: F)
-                           -> Region<'tcx>
+    fn combine_vars<F>(&self,
+                       t: CombineMapType,
+                       a: Region<'tcx>,
+                       b: Region<'tcx>,
+                       origin: SubregionOrigin<'tcx>,
+                       mut relate: F)
+                       -> Region<'tcx>
         where F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region<'tcx>, Region<'tcx>)
     {
         let vars = TwoRegions { a: a, b: b };

From 04ac507c990342905b0ed140de7c27d72df81bc1 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 28 Aug 2017 05:56:05 -0400
Subject: [PATCH 27/63] region_inference: simplify LUB and GLB interface
 slightly

---
 src/librustc/infer/region_inference/mod.rs | 52 ++++++++++++++--------
 1 file changed, 34 insertions(+), 18 deletions(-)

diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index f57ddc57b96e7..fb2652f290b51 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -571,15 +571,16 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
     pub fn make_subregion(&self,
                           origin: SubregionOrigin<'tcx>,
-                          _param_env: ty::ParamEnv<'tcx>,
+                          param_env: ty::ParamEnv<'tcx>,
                           sub: Region<'tcx>,
                           sup: Region<'tcx>) {
         // cannot add constraints once regions are resolved
         assert!(self.values_are_none());
 
-        debug!("RegionVarBindings: make_subregion({:?}, {:?}) due to {:?}",
+        debug!("RegionVarBindings: make_subregion({:?}, {:?}) in {:?} due to {:?}",
                sub,
                sup,
+               param_env,
                origin);
 
         match (sub, sup) {
@@ -622,6 +623,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         });
     }
 
+    /// Returns a new region that is the union of `a` and `b`,
+    /// or at least the closest we can approximate.
     pub fn lub_regions(&self,
                        origin: SubregionOrigin<'tcx>,
                        param_env: ty::ParamEnv<'tcx>,
@@ -642,13 +645,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             }
 
             _ => {
-                self.combine_vars(CombineMapType::Lub, a, b, origin.clone(), |this, old_r, new_r| {
-                    this.make_subregion(origin.clone(), param_env, old_r, new_r)
-                })
+                self.combine_vars(CombineMapType::Lub, param_env, a, b, origin.clone())
             }
         }
     }
 
+    /// Returns a new region that is the intersection of `a` and `b`,
+    /// or at least the closest we can approximate.
     pub fn glb_regions(&self,
                        origin: SubregionOrigin<'tcx>,
                        param_env: ty::ParamEnv<'tcx>,
@@ -669,9 +672,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             }
 
             _ => {
-                self.combine_vars(CombineMapType::Glb, a, b, origin.clone(), |this, old_r, new_r| {
-                    this.make_subregion(origin.clone(), param_env, new_r, old_r)
-                })
+                self.combine_vars(CombineMapType::Glb, param_env, a, b, origin.clone())
             }
         }
     }
@@ -703,14 +704,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn combine_vars<F>(&self,
-                       t: CombineMapType,
-                       a: Region<'tcx>,
-                       b: Region<'tcx>,
-                       origin: SubregionOrigin<'tcx>,
-                       mut relate: F)
-                       -> Region<'tcx>
-        where F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region<'tcx>, Region<'tcx>)
+    fn combine_vars(&self,
+                    t: CombineMapType,
+                    param_env: ty::ParamEnv<'tcx>,
+                    a: Region<'tcx>,
+                    b: Region<'tcx>,
+                    origin: SubregionOrigin<'tcx>)
+                    -> Region<'tcx>
     {
         let vars = TwoRegions { a: a, b: b };
         if let Some(&c) = self.combine_map(t).borrow().get(&vars) {
@@ -724,8 +724,24 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         if self.in_snapshot() {
             self.undo_log.borrow_mut().push(AddCombination(t, vars));
         }
-        relate(self, a, self.tcx.mk_region(ReVar(c)));
-        relate(self, b, self.tcx.mk_region(ReVar(c)));
+        let re_c = self.tcx.mk_region(ReVar(c));
+        match t {
+            CombineMapType::Glb => {
+                // c is the *intersection* of a and b, and hence
+                // smaller than both of them, so add constraint that c
+                // <= a and c <= b.
+                self.make_subregion(origin.clone(), param_env, re_c, a);
+                self.make_subregion(origin.clone(), param_env, re_c, b);
+            }
+
+            CombineMapType::Lub => {
+                // c is the *union* of a and b, and hence
+                // larger than both of them, so add constraint that a
+                // <= c and b <= c.
+                self.make_subregion(origin.clone(), param_env, a, re_c);
+                self.make_subregion(origin.clone(), param_env, b, re_c);
+            }
+        }
         debug!("combine_vars() c={:?}", c);
         self.tcx.mk_region(ReVar(c))
     }

From 3d72ae31ca9810d4c27be060ae6464fc8c8aa57b Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Mon, 28 Aug 2017 06:08:16 -0400
Subject: [PATCH 28/63] introduce `NormalizedRegions` to combine-vars
 (currently a no-op)

---
 src/librustc/infer/region_inference/mod.rs | 21 ++++++++++++---------
 src/librustc/ty/mod.rs                     | 21 +++++++++++++++++++++
 2 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index fb2652f290b51..9d4ea639285eb 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -100,9 +100,9 @@ pub enum VerifyBound<'tcx> {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-struct TwoRegions<'tcx> {
-    a: Region<'tcx>,
-    b: Region<'tcx>,
+struct NormalizedRegions {
+    a: ty::NormalizedRegion,
+    b: ty::NormalizedRegion,
 }
 
 #[derive(Copy, Clone, PartialEq)]
@@ -128,7 +128,7 @@ enum UndoLogEntry<'tcx> {
     AddGiven(Region<'tcx>, ty::RegionVid),
 
     /// We added a GLB/LUB "combination variable"
-    AddCombination(CombineMapType, TwoRegions<'tcx>),
+    AddCombination(CombineMapType, NormalizedRegions),
 }
 
 #[derive(Copy, Clone, PartialEq)]
@@ -168,7 +168,7 @@ pub enum ProcessedErrorOrigin<'tcx> {
     VariableFailure(RegionVariableOrigin),
 }
 
-type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
+type CombineMap = FxHashMap<NormalizedRegions, RegionVid>;
 
 #[derive(Clone, Debug)]
 struct RegionVariableInfo {
@@ -211,8 +211,8 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     /// a bit of a hack but seems to work.
     givens: RefCell<FxHashSet<(Region<'tcx>, ty::RegionVid)>>,
 
-    lubs: RefCell<CombineMap<'tcx>>,
-    glbs: RefCell<CombineMap<'tcx>>,
+    lubs: RefCell<CombineMap>,
+    glbs: RefCell<CombineMap>,
     bound_count: Cell<u32>,
 
     /// The undo log records actions that might later be undone.
@@ -697,7 +697,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         self.tcx.mk_region(ty::ReVar(vid))
     }
 
-    fn combine_map(&self, t: CombineMapType) -> &RefCell<CombineMap<'tcx>> {
+    fn combine_map(&self, t: CombineMapType) -> &RefCell<CombineMap> {
         match t {
             CombineMapType::Glb => &self.glbs,
             CombineMapType::Lub => &self.lubs,
@@ -712,7 +712,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                     origin: SubregionOrigin<'tcx>)
                     -> Region<'tcx>
     {
-        let vars = TwoRegions { a: a, b: b };
+        let vars = NormalizedRegions {
+            a: param_env.normalize_region(a),
+            b: param_env.normalize_region(b)
+        };
         if let Some(&c) = self.combine_map(t).borrow().get(&vars) {
             return self.tcx.mk_region(ReVar(c));
         }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index d6563651b075d..666a20df15ec6 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1381,6 +1381,27 @@ impl<'tcx> ParamEnv<'tcx> {
             }
         }
     }
+
+    pub fn normalize_region(self, region: ty::Region<'tcx>) -> NormalizedRegion {
+        NormalizedRegion {
+            region: *region
+        }
+    }
+}
+
+/// Normally, the meaning of a region is defined *relative* to some
+/// environment -- this is particular true with late-bound regions,
+/// where region depth requires a parameter environment to
+/// interpret. (See late-bound regions for more details.)
+///
+/// But there are times when it is convenient to be able to reference
+/// a region independently and know what universe it is in. For such
+/// cases, the region can be converted to a "normalized region". The
+/// primary effect of this is to convert *from* deBruijn indexing in
+/// LBR to storing the universe directly.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct NormalizedRegion {
+    region: RegionKind
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]

From 7b9f462a7345d526faf153fc9c03fa36205250d8 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Tue, 29 Aug 2017 10:04:05 -0400
Subject: [PATCH 29/63] move taint logic to its own file

---
 src/librustc/infer/higher_ranked/mod.rs      |   2 +-
 src/librustc/infer/region_inference/mod.rs   | 135 +---------------
 src/librustc/infer/region_inference/taint.rs | 160 +++++++++++++++++++
 3 files changed, 162 insertions(+), 135 deletions(-)
 create mode 100644 src/librustc/infer/region_inference/taint.rs

diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index d798414c268d5..a26a6d66683c9 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -16,7 +16,7 @@ use super::{CombinedSnapshot,
             HigherRankedType,
             SkolemizationMap};
 use super::combine::CombineFields;
-use super::region_inference::{TaintDirections};
+use super::region_inference::taint::TaintDirections;
 
 use ty::{self, TyCtxt, Binder, TypeFoldable};
 use ty::relate::{Relate, RelateResult, TypeRelation};
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 9d4ea639285eb..26c1a6d18936c 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -33,6 +33,7 @@ use std::fmt;
 use std::u32;
 
 mod graphviz;
+pub mod taint;
 
 /// A constraint that influences the inference process.
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
@@ -238,113 +239,6 @@ pub struct RegionSnapshot {
     region_snapshot: ut::Snapshot<ut::InPlace<ty::RegionVid>>,
 }
 
-/// When working with skolemized regions, we often wish to find all of
-/// the regions that are either reachable from a skolemized region, or
-/// which can reach a skolemized region, or both. We call such regions
-/// *tained* regions.  This struct allows you to decide what set of
-/// tainted regions you want.
-#[derive(Debug)]
-pub struct TaintDirections {
-    incoming: bool,
-    outgoing: bool,
-}
-
-impl TaintDirections {
-    pub fn incoming() -> Self {
-        TaintDirections { incoming: true, outgoing: false }
-    }
-
-    pub fn outgoing() -> Self {
-        TaintDirections { incoming: false, outgoing: true }
-    }
-
-    pub fn both() -> Self {
-        TaintDirections { incoming: true, outgoing: true }
-    }
-}
-
-struct TaintSet<'tcx> {
-    directions: TaintDirections,
-    regions: FxHashSet<ty::Region<'tcx>>
-}
-
-impl<'a, 'gcx, 'tcx> TaintSet<'tcx> {
-    fn new(directions: TaintDirections,
-           initial_region: ty::Region<'tcx>)
-           -> Self {
-        let mut regions = FxHashSet();
-        regions.insert(initial_region);
-        TaintSet { directions: directions, regions: regions }
-    }
-
-    fn fixed_point(&mut self,
-                   tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                   undo_log: &[UndoLogEntry<'tcx>],
-                   verifys: &[Verify<'tcx>]) {
-        let mut prev_len = 0;
-        while prev_len < self.len() {
-            debug!("tainted: prev_len = {:?} new_len = {:?}",
-                   prev_len, self.len());
-
-            prev_len = self.len();
-
-            for undo_entry in undo_log {
-                match undo_entry {
-                    &AddConstraint(ConstrainVarSubVar(a, b)) => {
-                        self.add_edge(tcx.mk_region(ReVar(a)),
-                                      tcx.mk_region(ReVar(b)));
-                    }
-                    &AddConstraint(ConstrainRegSubVar(a, b)) => {
-                        self.add_edge(a, tcx.mk_region(ReVar(b)));
-                    }
-                    &AddConstraint(ConstrainVarSubReg(a, b)) => {
-                        self.add_edge(tcx.mk_region(ReVar(a)), b);
-                    }
-                    &AddConstraint(ConstrainRegSubReg(a, b)) => {
-                        self.add_edge(a, b);
-                    }
-                    &AddGiven(a, b) => {
-                        self.add_edge(a, tcx.mk_region(ReVar(b)));
-                    }
-                    &AddVerify(i) => {
-                        verifys[i].bound.for_each_region(&mut |b| {
-                            self.add_edge(verifys[i].region, b);
-                        });
-                    }
-                    &AddCombination(..) |
-                    &AddVar(..) |
-                    &OpenSnapshot |
-                    &CommitedSnapshot => {}
-                }
-            }
-        }
-    }
-
-    fn into_set(self) -> FxHashSet<ty::Region<'tcx>> {
-        self.regions
-    }
-
-    fn len(&self) -> usize {
-        self.regions.len()
-    }
-
-    fn add_edge(&mut self,
-                source: ty::Region<'tcx>,
-                target: ty::Region<'tcx>) {
-        if self.directions.incoming {
-            if self.regions.contains(&target) {
-                self.regions.insert(source);
-            }
-        }
-
-        if self.directions.outgoing {
-            if self.regions.contains(&source) {
-                self.regions.insert(target);
-            }
-        }
-    }
-}
-
 impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> RegionVarBindings<'a, 'gcx, 'tcx> {
         RegionVarBindings {
@@ -782,33 +676,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             .collect()
     }
 
-    /// Computes all regions that have been related to `r0` since the
-    /// mark `mark` was made---`r0` itself will be the first
-    /// entry. The `directions` parameter controls what kind of
-    /// relations are considered. For example, one can say that only
-    /// "incoming" edges to `r0` are desired, in which case one will
-    /// get the set of regions `{r|r <= r0}`. This is used when
-    /// checking whether skolemized regions are being improperly
-    /// related to other regions.
-    pub fn tainted(&self,
-                   mark: &RegionSnapshot,
-                   r0: Region<'tcx>,
-                   directions: TaintDirections)
-                   -> FxHashSet<ty::Region<'tcx>> {
-        debug!("tainted(mark={:?}, r0={:?}, directions={:?})",
-               mark, r0, directions);
-
-        // `result_set` acts as a worklist: we explore all outgoing
-        // edges and add any new regions we find to result_set.  This
-        // is not a terribly efficient implementation.
-        let mut taint_set = TaintSet::new(directions, r0);
-        taint_set.fixed_point(self.tcx,
-                              &self.undo_log.borrow()[mark.length..],
-                              &self.verifys.borrow());
-        debug!("tainted: result={:?}", taint_set.regions);
-        return taint_set.into_set();
-    }
-
     /// This function performs the actual region resolution.  It must be
     /// called after all constraints have been added.  It performs a
     /// fixed-point iteration to find region values which satisfy all
diff --git a/src/librustc/infer/region_inference/taint.rs b/src/librustc/infer/region_inference/taint.rs
new file mode 100644
index 0000000000000..06646c9c910ea
--- /dev/null
+++ b/src/librustc/infer/region_inference/taint.rs
@@ -0,0 +1,160 @@
+// 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.
+
+//! The "Tainted" code is used for computing the LUB/GLB for
+//! higher-ranked types. See the README for `infer::higher_ranked` for
+//! more information.
+
+use super::{AddCombination, AddConstraint, AddGiven, AddVar, AddVerify,
+            CommitedSnapshot,
+            ConstrainVarSubVar, ConstrainVarSubReg, ConstrainRegSubReg, ConstrainRegSubVar,
+            OpenSnapshot,
+            RegionSnapshot, RegionVarBindings,
+            UndoLogEntry,
+            Verify};
+
+use ty::{self, TyCtxt, Region, ReVar};
+use rustc_data_structures::fx::FxHashSet;
+
+/// When working with skolemized regions, we often wish to find all of
+/// the regions that are either reachable from a skolemized region, or
+/// which can reach a skolemized region, or both. We call such regions
+/// *tained* regions.  This struct allows you to decide what set of
+/// tainted regions you want.
+#[derive(Debug)]
+pub struct TaintDirections {
+    incoming: bool,
+    outgoing: bool,
+}
+
+impl TaintDirections {
+    pub fn incoming() -> Self {
+        TaintDirections { incoming: true, outgoing: false }
+    }
+
+    pub fn outgoing() -> Self {
+        TaintDirections { incoming: false, outgoing: true }
+    }
+
+    pub fn both() -> Self {
+        TaintDirections { incoming: true, outgoing: true }
+    }
+}
+
+struct TaintSet<'tcx> {
+    directions: TaintDirections,
+    regions: FxHashSet<ty::Region<'tcx>>
+}
+
+impl<'a, 'gcx, 'tcx> TaintSet<'tcx> {
+    fn new(directions: TaintDirections,
+           initial_region: ty::Region<'tcx>)
+           -> Self {
+        let mut regions = FxHashSet();
+        regions.insert(initial_region);
+        TaintSet { directions: directions, regions: regions }
+    }
+
+    fn fixed_point(&mut self,
+                   tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                   undo_log: &[UndoLogEntry<'tcx>],
+                   verifys: &[Verify<'tcx>]) {
+        let mut prev_len = 0;
+        while prev_len < self.len() {
+            debug!("tainted: prev_len = {:?} new_len = {:?}",
+                   prev_len, self.len());
+
+            prev_len = self.len();
+
+            for undo_entry in undo_log {
+                match undo_entry {
+                    &AddConstraint(ConstrainVarSubVar(a, b)) => {
+                        self.add_edge(tcx.mk_region(ReVar(a)),
+                                      tcx.mk_region(ReVar(b)));
+                    }
+                    &AddConstraint(ConstrainRegSubVar(a, b)) => {
+                        self.add_edge(a, tcx.mk_region(ReVar(b)));
+                    }
+                    &AddConstraint(ConstrainVarSubReg(a, b)) => {
+                        self.add_edge(tcx.mk_region(ReVar(a)), b);
+                    }
+                    &AddConstraint(ConstrainRegSubReg(a, b)) => {
+                        self.add_edge(a, b);
+                    }
+                    &AddGiven(a, b) => {
+                        self.add_edge(a, tcx.mk_region(ReVar(b)));
+                    }
+                    &AddVerify(i) => {
+                        verifys[i].bound.for_each_region(&mut |b| {
+                            self.add_edge(verifys[i].region, b);
+                        });
+                    }
+                    &AddCombination(..) |
+                    &AddVar(..) |
+                    &OpenSnapshot |
+                    &CommitedSnapshot => {}
+                }
+            }
+        }
+    }
+
+    fn into_set(self) -> FxHashSet<ty::Region<'tcx>> {
+        self.regions
+    }
+
+    fn len(&self) -> usize {
+        self.regions.len()
+    }
+
+    fn add_edge(&mut self,
+                source: ty::Region<'tcx>,
+                target: ty::Region<'tcx>) {
+        if self.directions.incoming {
+            if self.regions.contains(&target) {
+                self.regions.insert(source);
+            }
+        }
+
+        if self.directions.outgoing {
+            if self.regions.contains(&source) {
+                self.regions.insert(target);
+            }
+        }
+    }
+}
+
+impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
+    /// Computes all regions that have been related to `r0` since the
+    /// mark `mark` was made---`r0` itself will be the first
+    /// entry. The `directions` parameter controls what kind of
+    /// relations are considered. For example, one can say that only
+    /// "incoming" edges to `r0` are desired, in which case one will
+    /// get the set of regions `{r|r <= r0}`. This is used when
+    /// checking whether skolemized regions are being improperly
+    /// related to other regions.
+    pub fn tainted(&self,
+                   mark: &RegionSnapshot,
+                   r0: Region<'tcx>,
+                   directions: TaintDirections)
+                   -> FxHashSet<ty::Region<'tcx>> {
+        debug!("tainted(mark={:?}, r0={:?}, directions={:?})",
+               mark, r0, directions);
+
+        // `result_set` acts as a worklist: we explore all outgoing
+        // edges and add any new regions we find to result_set.  This
+        // is not a terribly efficient implementation.
+        let mut taint_set = TaintSet::new(directions, r0);
+        taint_set.fixed_point(self.tcx,
+                              &self.undo_log.borrow()[mark.length..],
+                              &self.verifys.borrow());
+        debug!("tainted: result={:?}", taint_set.regions);
+        return taint_set.into_set();
+    }
+}

From 55c5ff8e83f4893ba4afc4bf1a814991c2c50945 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Tue, 29 Aug 2017 18:05:00 -0400
Subject: [PATCH 30/63] track `ParamEnv` with region constraints

---
 .../infer/region_inference/graphviz.rs        |  8 ++---
 src/librustc/infer/region_inference/mod.rs    | 36 +++++++++----------
 src/librustc/infer/region_inference/taint.rs  |  8 ++---
 3 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/src/librustc/infer/region_inference/graphviz.rs b/src/librustc/infer/region_inference/graphviz.rs
index 49f57d9aef50e..3ac9dc1a1ab0b 100644
--- a/src/librustc/infer/region_inference/graphviz.rs
+++ b/src/librustc/infer/region_inference/graphviz.rs
@@ -211,13 +211,13 @@ impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
 
 fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
     match *c {
-        Constraint::ConstrainVarSubVar(rv_1, rv_2) =>
+        Constraint::ConstrainVarSubVar(_, rv_1, rv_2) =>
             (Node::RegionVid(rv_1), Node::RegionVid(rv_2)),
-        Constraint::ConstrainRegSubVar(r_1, rv_2) =>
+        Constraint::ConstrainRegSubVar(_, r_1, rv_2) =>
             (Node::Region(*r_1), Node::RegionVid(rv_2)),
-        Constraint::ConstrainVarSubReg(rv_1, r_2) =>
+        Constraint::ConstrainVarSubReg(_, rv_1, r_2) =>
             (Node::RegionVid(rv_1), Node::Region(*r_2)),
-        Constraint::ConstrainRegSubReg(r_1, r_2) =>
+        Constraint::ConstrainRegSubReg(_, r_1, r_2) =>
             (Node::Region(*r_1), Node::Region(*r_2)),
     }
 }
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 26c1a6d18936c..8510821942283 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -39,20 +39,20 @@ pub mod taint;
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
 pub enum Constraint<'tcx> {
     /// One region variable is subregion of another
-    ConstrainVarSubVar(RegionVid, RegionVid),
+    ConstrainVarSubVar(ty::ParamEnv<'tcx>, RegionVid, RegionVid),
 
     /// Concrete region is subregion of region variable
-    ConstrainRegSubVar(Region<'tcx>, RegionVid),
+    ConstrainRegSubVar(ty::ParamEnv<'tcx>, Region<'tcx>, RegionVid),
 
     /// Region variable is subregion of concrete region. This does not
     /// directly affect inference, but instead is checked after
     /// inference is complete.
-    ConstrainVarSubReg(RegionVid, Region<'tcx>),
+    ConstrainVarSubReg(ty::ParamEnv<'tcx>, RegionVid, Region<'tcx>),
 
     /// A constraint where neither side is a variable. This does not
     /// directly affect inference, but instead is checked after
     /// inference is complete.
-    ConstrainRegSubReg(Region<'tcx>, Region<'tcx>),
+    ConstrainRegSubReg(ty::ParamEnv<'tcx>, Region<'tcx>, Region<'tcx>),
 }
 
 /// VerifyGenericBound(T, _, R, RS): The parameter type `T` (or
@@ -489,16 +489,16 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                 // all regions are subregions of static, so we can ignore this
             }
             (&ReVar(sub_id), &ReVar(sup_id)) => {
-                self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), origin);
+                self.add_constraint(ConstrainVarSubVar(param_env, sub_id, sup_id), origin);
             }
             (_, &ReVar(sup_id)) => {
-                self.add_constraint(ConstrainRegSubVar(sub, sup_id), origin);
+                self.add_constraint(ConstrainRegSubVar(param_env, sub, sup_id), origin);
             }
             (&ReVar(sub_id), _) => {
-                self.add_constraint(ConstrainVarSubReg(sub_id, sup), origin);
+                self.add_constraint(ConstrainVarSubReg(param_env, sub_id, sup), origin);
             }
             _ => {
-                self.add_constraint(ConstrainRegSubReg(sub, sup), origin);
+                self.add_constraint(ConstrainRegSubReg(param_env, sub, sup), origin);
             }
         }
     }
@@ -865,11 +865,11 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             debug!("expansion: constraint={:?} origin={:?}",
                    constraint, origin);
             match *constraint {
-                ConstrainRegSubVar(a_region, b_vid) => {
+                ConstrainRegSubVar(_, a_region, b_vid) => {
                     let b_data = &mut var_values[b_vid.index as usize];
                     self.expand_node(region_rels, a_region, b_vid, b_data)
                 }
-                ConstrainVarSubVar(a_vid, b_vid) => {
+                ConstrainVarSubVar(_, a_vid, b_vid) => {
                     match var_values[a_vid.index as usize] {
                         ErrorValue => false,
                         Value(a_region) => {
@@ -980,7 +980,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                     // Expansion will ensure that these constraints hold. Ignore.
                 }
 
-                ConstrainRegSubReg(sub, sup) => {
+                ConstrainRegSubReg(_, sub, sup) => {
                     if region_rels.is_subregion_of(sub, sup) {
                         continue;
                     }
@@ -994,7 +994,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                     errors.push(ConcreteFailure((*origin).clone(), sub, sup));
                 }
 
-                ConstrainVarSubReg(a_vid, b_region) => {
+                ConstrainVarSubReg(_, a_vid, b_region) => {
                     let a_data = &mut var_data[a_vid.index as usize];
                     debug!("contraction: {:?} == {:?}, {:?}",
                            a_vid,
@@ -1131,15 +1131,15 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
         for (constraint, _) in constraints.iter() {
             match *constraint {
-                ConstrainVarSubVar(a_id, b_id) => {
+                ConstrainVarSubVar(_, a_id, b_id) => {
                     graph.add_edge(NodeIndex(a_id.index as usize),
                                    NodeIndex(b_id.index as usize),
                                    *constraint);
                 }
-                ConstrainRegSubVar(_, b_id) => {
+                ConstrainRegSubVar(_, _, b_id) => {
                     graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint);
                 }
-                ConstrainVarSubReg(a_id, _) => {
+                ConstrainVarSubReg(_, a_id, _) => {
                     graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint);
                 }
                 ConstrainRegSubReg(..) => {
@@ -1269,7 +1269,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             let source_node_index = NodeIndex(source_vid.index as usize);
             for (_, edge) in graph.adjacent_edges(source_node_index, dir) {
                 match edge.data {
-                    ConstrainVarSubVar(from_vid, to_vid) => {
+                    ConstrainVarSubVar(_, from_vid, to_vid) => {
                         let opp_vid = if from_vid == source_vid {
                             to_vid
                         } else {
@@ -1280,8 +1280,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                         }
                     }
 
-                    ConstrainRegSubVar(region, _) |
-                    ConstrainVarSubReg(_, region) => {
+                    ConstrainRegSubVar(_, region, _) |
+                    ConstrainVarSubReg(_, _, region) => {
                         state.result.push(RegionAndOrigin {
                             region,
                             origin: this.constraints.borrow().get(&edge.data).unwrap().clone(),
diff --git a/src/librustc/infer/region_inference/taint.rs b/src/librustc/infer/region_inference/taint.rs
index 06646c9c910ea..963efa61144c6 100644
--- a/src/librustc/infer/region_inference/taint.rs
+++ b/src/librustc/infer/region_inference/taint.rs
@@ -75,17 +75,17 @@ impl<'a, 'gcx, 'tcx> TaintSet<'tcx> {
 
             for undo_entry in undo_log {
                 match undo_entry {
-                    &AddConstraint(ConstrainVarSubVar(a, b)) => {
+                    &AddConstraint(ConstrainVarSubVar(_, a, b)) => {
                         self.add_edge(tcx.mk_region(ReVar(a)),
                                       tcx.mk_region(ReVar(b)));
                     }
-                    &AddConstraint(ConstrainRegSubVar(a, b)) => {
+                    &AddConstraint(ConstrainRegSubVar(_, a, b)) => {
                         self.add_edge(a, tcx.mk_region(ReVar(b)));
                     }
-                    &AddConstraint(ConstrainVarSubReg(a, b)) => {
+                    &AddConstraint(ConstrainVarSubReg(_, a, b)) => {
                         self.add_edge(tcx.mk_region(ReVar(a)), b);
                     }
-                    &AddConstraint(ConstrainRegSubReg(a, b)) => {
+                    &AddConstraint(ConstrainRegSubReg(_, a, b)) => {
                         self.add_edge(a, b);
                     }
                     &AddGiven(a, b) => {

From fc98001c133a7ea41ae25d6125badaf22e803a23 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 30 Aug 2017 12:18:25 -0400
Subject: [PATCH 31/63] WIP rewrite to TaintIterator -- seems to work

---
 src/librustc/infer/fudge.rs                  |   2 +-
 src/librustc/infer/higher_ranked/mod.rs      |  88 +++++----
 src/librustc/infer/region_inference/mod.rs   |  14 --
 src/librustc/infer/region_inference/taint.rs | 192 +++++++++----------
 4 files changed, 138 insertions(+), 158 deletions(-)

diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs
index e84ef58d05034..f8ed373f72d9c 100644
--- a/src/librustc/infer/fudge.rs
+++ b/src/librustc/infer/fudge.rs
@@ -156,7 +156,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> {
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
             ty::ReVar(v) if self.region_vars.contains(&v) => {
-                // TODO -- I am not entirely sur how fudging and
+                // FIXME -- I am not entirely sure how fudging and
                 // universes should work, but using root is a
                 // conservative choice here, and I suspect it doesn't
                 // much matter.
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index a26a6d66683c9..30e15133b0f09 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -16,10 +16,11 @@ use super::{CombinedSnapshot,
             HigherRankedType,
             SkolemizationMap};
 use super::combine::CombineFields;
-use super::region_inference::taint::TaintDirections;
+use super::region_inference::taint::TaintIterator;
 
 use ty::{self, TyCtxt, Binder, TypeFoldable};
 use ty::relate::{Relate, RelateResult, TypeRelation};
+use std::usize;
 use syntax_pos::Span;
 use util::nodemap::{FxHashMap, FxHashSet};
 
@@ -108,7 +109,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
                 fold_regions_in(
                     self.tcx(),
                     &result0,
-                    |r, debruijn| generalize_region(self.infcx, span, snapshot, debruijn,
+                    |r, debruijn| generalize_region(self.infcx, span, snapshot, param_env, debruijn,
                                                     &new_vars, &a_map, r));
 
             debug!("lub({:?},{:?}) = {:?}",
@@ -122,6 +123,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         fn generalize_region<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                              span: Span,
                                              snapshot: &CombinedSnapshot<'a, 'tcx>,
+                                             param_env: ty::ParamEnv<'tcx>,
                                              debruijn: ty::DebruijnIndex,
                                              new_vars: &[ty::RegionVid],
                                              a_map: &FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
@@ -134,33 +136,39 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
                 return r0;
             }
 
-            let tainted = infcx.tainted_regions(snapshot, r0, TaintDirections::both());
+            let mut best_index = usize::MAX;
 
-            // Variables created during LUB computation which are
-            // *related* to regions that pre-date the LUB computation
-            // stay as they are.
-            if !tainted.iter().all(|&r| is_var_in_set(new_vars, r)) {
-                debug!("generalize_region(r0={:?}): \
-                        non-new-variables found in {:?}",
-                       r0, tainted);
-                assert!(!r0.is_late_bound());
-                return r0;
-            }
-
-            // Otherwise, the variable must be associated with at
-            // least one of the variables representing bound regions
-            // in both A and B.  Replace the variable with the "first"
-            // bound region from A that we find it to be associated
-            // with.
-            for (a_br, a_r) in a_map {
-                if tainted.iter().any(|x| x == a_r) {
+            for r in infcx.tainted_regions(snapshot, param_env, r0) {
+                // Variables created during LUB computation which are
+                // *related* to regions that pre-date the LUB computation
+                // stay as they are.
+                if !is_var_in_set(new_vars, r) {
                     debug!("generalize_region(r0={:?}): \
-                            replacing with {:?}, tainted={:?}",
-                           r0, *a_br, tainted);
-                    return infcx.tcx.mk_region(ty::ReLateBound(debruijn, *a_br));
+                            non-new-variable `{:?}` found in taint regions",
+                           r0, r);
+                    assert!(!r0.is_late_bound());
+                    return r0;
+                }
+
+                // Otherwise, the variable must be associated with at
+                // least one of the variables representing bound regions
+                // in both A and B.  Replace the variable with the "first"
+                // bound region from A that we find it to be associated
+                // with.
+                for (index, (_, &a_r)) in a_map.iter().enumerate() {
+                    if r == a_r && index < best_index {
+                        best_index = index;
+                    }
                 }
             }
 
+            for (a_br, a_r) in a_map.iter().skip(best_index) {
+                debug!("generalize_region(r0={:?}): \
+                        replacing with {:?}, tainted={:?}",
+                       r0, *a_br, a_r);
+                return infcx.tcx.mk_region(ty::ReLateBound(debruijn, *a_br));
+            }
+
             span_bug!(
                 span,
                 "region {:?} is not associated with any bound region from A!",
@@ -206,8 +214,8 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
                 fold_regions_in(
                     self.tcx(),
                     &result0,
-                    |r, debruijn| generalize_region(self.infcx, span, snapshot, debruijn,
-                                                    &new_vars,
+                    |r, debruijn| generalize_region(self.infcx, span, snapshot, param_env,
+                                                    debruijn, &new_vars,
                                                     &a_map, &a_vars, &b_vars,
                                                     r));
 
@@ -222,6 +230,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         fn generalize_region<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                              span: Span,
                                              snapshot: &CombinedSnapshot<'a, 'tcx>,
+                                             param_env: ty::ParamEnv<'tcx>,
                                              debruijn: ty::DebruijnIndex,
                                              new_vars: &[ty::RegionVid],
                                              a_map: &FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
@@ -234,25 +243,24 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
                 return r0;
             }
 
-            let tainted = infcx.tainted_regions(snapshot, r0, TaintDirections::both());
-
             let mut a_r = None;
             let mut b_r = None;
             let mut only_new_vars = true;
-            for r in &tainted {
-                if is_var_in_set(a_vars, *r) {
+
+            for r in infcx.tainted_regions(snapshot, param_env, r0) {
+                if is_var_in_set(a_vars, r) {
                     if a_r.is_some() {
                         return fresh_bound_variable(infcx, debruijn);
                     } else {
-                        a_r = Some(*r);
+                        a_r = Some(r);
                     }
-                } else if is_var_in_set(b_vars, *r) {
+                } else if is_var_in_set(b_vars, r) {
                     if b_r.is_some() {
                         return fresh_bound_variable(infcx, debruijn);
                     } else {
-                        b_r = Some(*r);
+                        b_r = Some(r);
                     }
-                } else if !is_var_in_set(new_vars, *r) {
+                } else if !is_var_in_set(new_vars, r) {
                     only_new_vars = false;
                 }
             }
@@ -359,12 +367,12 @@ fn fold_regions_in<'a, 'gcx, 'tcx, T, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
 }
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
-    fn tainted_regions(&self,
-                       snapshot: &CombinedSnapshot<'a, 'tcx>,
-                       r: ty::Region<'tcx>,
-                       directions: TaintDirections)
-                       -> FxHashSet<ty::Region<'tcx>> {
-        self.region_vars.tainted(&snapshot.region_vars_snapshot, r, directions)
+    fn tainted_regions<'this>(&'this self,
+                              snapshot: &CombinedSnapshot<'a, 'tcx>,
+                              param_env: ty::ParamEnv<'tcx>,
+                              r: ty::Region<'tcx>)
+                              -> TaintIterator<'this, 'gcx, 'tcx> {
+        self.region_vars.tainted(&snapshot.region_vars_snapshot, param_env, r)
     }
 
     fn region_vars_confined_to_snapshot(&self,
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 8510821942283..1d4a1e2342f7d 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -1379,20 +1379,6 @@ impl<'a, 'gcx, 'tcx> GenericKind<'tcx> {
 }
 
 impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> {
-    fn for_each_region(&self, f: &mut FnMut(ty::Region<'tcx>)) {
-        match self {
-            &VerifyBound::AnyRegion(ref rs) |
-            &VerifyBound::AllRegions(ref rs) => for &r in rs {
-                f(r);
-            },
-
-            &VerifyBound::AnyBound(ref bs) |
-            &VerifyBound::AllBounds(ref bs) => for b in bs {
-                b.for_each_region(f);
-            },
-        }
-    }
-
     pub fn must_hold(&self) -> bool {
         match self {
             &VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic),
diff --git a/src/librustc/infer/region_inference/taint.rs b/src/librustc/infer/region_inference/taint.rs
index 963efa61144c6..43497b01443ae 100644
--- a/src/librustc/infer/region_inference/taint.rs
+++ b/src/librustc/infer/region_inference/taint.rs
@@ -17,116 +17,107 @@ use super::{AddCombination, AddConstraint, AddGiven, AddVar, AddVerify,
             ConstrainVarSubVar, ConstrainVarSubReg, ConstrainRegSubReg, ConstrainRegSubVar,
             OpenSnapshot,
             RegionSnapshot, RegionVarBindings,
-            UndoLogEntry,
-            Verify};
+            UndoLogEntry};
 
+use std::cell::Ref;
 use ty::{self, TyCtxt, Region, ReVar};
 use rustc_data_structures::fx::FxHashSet;
 
-/// When working with skolemized regions, we often wish to find all of
-/// the regions that are either reachable from a skolemized region, or
-/// which can reach a skolemized region, or both. We call such regions
-/// *tained* regions.  This struct allows you to decide what set of
-/// tainted regions you want.
-#[derive(Debug)]
-pub struct TaintDirections {
-    incoming: bool,
-    outgoing: bool,
+pub(in infer) struct TaintIterator<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    undo_log: Ref<'a, [UndoLogEntry<'tcx>]>,
+    regions: FxHashSet<ty::NormalizedRegion>,
+    queue: Vec<ty::Region<'tcx>>,
 }
 
-impl TaintDirections {
-    pub fn incoming() -> Self {
-        TaintDirections { incoming: true, outgoing: false }
-    }
-
-    pub fn outgoing() -> Self {
-        TaintDirections { incoming: false, outgoing: true }
-    }
-
-    pub fn both() -> Self {
-        TaintDirections { incoming: true, outgoing: true }
-    }
-}
-
-struct TaintSet<'tcx> {
-    directions: TaintDirections,
-    regions: FxHashSet<ty::Region<'tcx>>
-}
-
-impl<'a, 'gcx, 'tcx> TaintSet<'tcx> {
-    fn new(directions: TaintDirections,
+impl<'a, 'gcx, 'tcx> TaintIterator<'a, 'gcx, 'tcx> {
+    fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+           undo_log: Ref<'a, [UndoLogEntry<'tcx>]>,
+           param_env: ty::ParamEnv<'tcx>,
            initial_region: ty::Region<'tcx>)
            -> Self {
         let mut regions = FxHashSet();
-        regions.insert(initial_region);
-        TaintSet { directions: directions, regions: regions }
+        let norm_initial_region = param_env.normalize_region(initial_region);
+        regions.insert(norm_initial_region);
+        let queue = vec![initial_region];
+        TaintIterator { tcx, undo_log, regions, queue }
     }
 
-    fn fixed_point(&mut self,
-                   tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                   undo_log: &[UndoLogEntry<'tcx>],
-                   verifys: &[Verify<'tcx>]) {
-        let mut prev_len = 0;
-        while prev_len < self.len() {
-            debug!("tainted: prev_len = {:?} new_len = {:?}",
-                   prev_len, self.len());
-
-            prev_len = self.len();
+    fn add_edge(regions: &mut FxHashSet<ty::NormalizedRegion>,
+                queue: &mut Vec<ty::Region<'tcx>>,
+                param_env: ty::ParamEnv<'tcx>,
+                source: ty::Region<'tcx>,
+                target: ty::Region<'tcx>) {
+        let norm_source = param_env.normalize_region(source);
+        let norm_target = param_env.normalize_region(target);
 
-            for undo_entry in undo_log {
-                match undo_entry {
-                    &AddConstraint(ConstrainVarSubVar(_, a, b)) => {
-                        self.add_edge(tcx.mk_region(ReVar(a)),
-                                      tcx.mk_region(ReVar(b)));
-                    }
-                    &AddConstraint(ConstrainRegSubVar(_, a, b)) => {
-                        self.add_edge(a, tcx.mk_region(ReVar(b)));
-                    }
-                    &AddConstraint(ConstrainVarSubReg(_, a, b)) => {
-                        self.add_edge(tcx.mk_region(ReVar(a)), b);
-                    }
-                    &AddConstraint(ConstrainRegSubReg(_, a, b)) => {
-                        self.add_edge(a, b);
-                    }
-                    &AddGiven(a, b) => {
-                        self.add_edge(a, tcx.mk_region(ReVar(b)));
-                    }
-                    &AddVerify(i) => {
-                        verifys[i].bound.for_each_region(&mut |b| {
-                            self.add_edge(verifys[i].region, b);
-                        });
-                    }
-                    &AddCombination(..) |
-                    &AddVar(..) |
-                    &OpenSnapshot |
-                    &CommitedSnapshot => {}
-                }
+        if regions.contains(&norm_target) {
+            if regions.insert(norm_source) {
+                queue.push(source);
             }
         }
-    }
 
-    fn into_set(self) -> FxHashSet<ty::Region<'tcx>> {
-        self.regions
-    }
-
-    fn len(&self) -> usize {
-        self.regions.len()
-    }
-
-    fn add_edge(&mut self,
-                source: ty::Region<'tcx>,
-                target: ty::Region<'tcx>) {
-        if self.directions.incoming {
-            if self.regions.contains(&target) {
-                self.regions.insert(source);
+        if regions.contains(&norm_source) {
+            if regions.insert(norm_target) {
+                queue.push(target);
             }
         }
+    }
+}
 
-        if self.directions.outgoing {
-            if self.regions.contains(&source) {
-                self.regions.insert(target);
+impl<'a, 'gcx, 'tcx> Iterator for TaintIterator<'a, 'gcx, 'tcx> {
+    type Item = ty::Region<'tcx>;
+
+    fn next(&mut self) -> Option<ty::Region<'tcx>> {
+        let region = match self.queue.pop() {
+            Some(r) => r,
+            None => return None,
+        };
+
+        for undo_entry in &self.undo_log[..] {
+            match undo_entry {
+                &AddConstraint(ConstrainVarSubVar(param_env, a, b)) => {
+                    Self::add_edge(&mut self.regions,
+                                   &mut self.queue,
+                                   param_env,
+                                   self.tcx.mk_region(ReVar(a)),
+                                   self.tcx.mk_region(ReVar(b)));
+                }
+                &AddConstraint(ConstrainRegSubVar(param_env, a, b)) => {
+                    Self::add_edge(&mut self.regions,
+                                   &mut self.queue,
+                                   param_env,
+                                   a,
+                                   self.tcx.mk_region(ReVar(b)));
+                }
+                &AddConstraint(ConstrainVarSubReg(param_env, a, b)) => {
+                    Self::add_edge(&mut self.regions,
+                                   &mut self.queue,
+                                   param_env,
+                                   self.tcx.mk_region(ReVar(a)),
+                                   b);
+                }
+                &AddConstraint(ConstrainRegSubReg(param_env, a, b)) => {
+                    Self::add_edge(&mut self.regions,
+                                   &mut self.queue,
+                                   param_env,
+                                   a,
+                                   b);
+                }
+                &AddGiven(..) => {
+                    bug!("cannot use taint when givens have been added")
+                }
+                &AddVerify(..) => {
+                    bug!("cannot use taint when verifys have been added")
+                }
+                &AddCombination(..) |
+                &AddVar(..) |
+                &OpenSnapshot |
+                &CommitedSnapshot => {}
             }
         }
+
+        Some(region)
     }
 }
 
@@ -139,22 +130,17 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
     /// get the set of regions `{r|r <= r0}`. This is used when
     /// checking whether skolemized regions are being improperly
     /// related to other regions.
-    pub fn tainted(&self,
-                   mark: &RegionSnapshot,
-                   r0: Region<'tcx>,
-                   directions: TaintDirections)
-                   -> FxHashSet<ty::Region<'tcx>> {
-        debug!("tainted(mark={:?}, r0={:?}, directions={:?})",
-               mark, r0, directions);
+    pub(in infer) fn tainted<'this>(&'this self,
+                                    mark: &RegionSnapshot,
+                                    param_env: ty::ParamEnv<'tcx>,
+                                    r0: Region<'tcx>)
+                                    -> TaintIterator<'this, 'gcx, 'tcx> {
+        debug!("tainted(mark={:?}, r0={:?})", mark, r0);
 
         // `result_set` acts as a worklist: we explore all outgoing
         // edges and add any new regions we find to result_set.  This
         // is not a terribly efficient implementation.
-        let mut taint_set = TaintSet::new(directions, r0);
-        taint_set.fixed_point(self.tcx,
-                              &self.undo_log.borrow()[mark.length..],
-                              &self.verifys.borrow());
-        debug!("tainted: result={:?}", taint_set.regions);
-        return taint_set.into_set();
+        let undo_log = Ref::map(self.undo_log.borrow(), |l| &l[mark.length..]);
+        TaintIterator::new(self.tcx, undo_log, param_env, r0)
     }
 }

From 7efe03f9f70539b88148a172aeebda7d40a46512 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 31 Aug 2017 09:03:06 -0400
Subject: [PATCH 32/63] we now handle higher-ranked subtyping correctly

Fixes #33684
---
 src/test/compile-fail/hr-subtype.rs                       | 8 ++++----
 .../compile-fail/regions-fn-subtyping-return-static.rs    | 6 +-----
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/src/test/compile-fail/hr-subtype.rs b/src/test/compile-fail/hr-subtype.rs
index c88d74d53ce94..e3077eb00406f 100644
--- a/src/test/compile-fail/hr-subtype.rs
+++ b/src/test/compile-fail/hr-subtype.rs
@@ -52,10 +52,6 @@ macro_rules! check {
             //[bound_inv_a_b_vs_bound_inv_a]~^^^ ERROR mismatched types
             //[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^ ERROR mismatched types
             //[free_inv_x_vs_free_inv_y]~^^^^^ ERROR mismatched types
-            //[bound_a_b_vs_bound_a]~^^^^^^ ERROR mismatched types
-            //[bound_co_a_b_vs_bound_co_a]~^^^^^^^ ERROR mismatched types
-            //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^ ERROR mismatched types
-            //[bound_co_a_co_b_ret_contra_a]~^^^^^^^^^ ERROR mismatched types
         }
     }
 }
@@ -113,4 +109,8 @@ fn main() {
 //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
 //[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
 //[free_x_vs_free_x]~^^^^^ ERROR compilation successful
+//[bound_a_b_vs_bound_a]~^^^^^^ ERROR compilation successful
+//[bound_co_a_b_vs_bound_co_a]~^^^^^^^ ERROR compilation successful
+//[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^ ERROR compilation successful
+//[bound_co_a_co_b_ret_contra_a]~^^^^^^^^^ ERROR compilation successful
 }
diff --git a/src/test/compile-fail/regions-fn-subtyping-return-static.rs b/src/test/compile-fail/regions-fn-subtyping-return-static.rs
index 6be65a5e35905..e63c679daf1e8 100644
--- a/src/test/compile-fail/regions-fn-subtyping-return-static.rs
+++ b/src/test/compile-fail/regions-fn-subtyping-return-static.rs
@@ -47,8 +47,7 @@ fn baz(x: &S) -> &S {
 fn supply_F() {
     want_F(foo);
 
-    // FIXME(#33684) -- this should be a subtype, but current alg. rejects it incorrectly
-    want_F(bar); //~ ERROR E0308
+    want_F(bar);
 
     want_F(baz);
 }
@@ -58,9 +57,6 @@ fn supply_G() {
     want_G(bar);
     want_G(baz);
     //~^ ERROR mismatched types
-    //~| expected type `for<'cx> fn(&'cx S) -> &'static S`
-    //~| found type `for<'r> fn(&'r S) -> &'r S {baz}`
-    //~| expected concrete lifetime, found bound lifetime parameter 'cx
 }
 
 pub fn main() {

From 21222fda4d7e3b7b38085a61cc879512a1c7216f Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 31 Aug 2017 09:03:55 -0400
Subject: [PATCH 33/63] FIXME: hack up the error messages so that compile-fail
 passes

They are still not very good. Will come back to that.
---
 src/librustc/infer/error_reporting/mod.rs     |  4 +-
 .../infer/error_reporting/skol_conflict.rs    | 54 +++++++++++++++++++
 src/librustc/ty/sty.rs                        |  7 +++
 .../compile-fail/associated-types-eq-hr.rs    |  8 +--
 .../higher-ranked-projection.rs               |  3 +-
 .../compile-fail/hrtb-conflate-regions.rs     |  2 +-
 ...tb-higher-ranker-supertraits-transitive.rs |  2 +-
 .../hrtb-higher-ranker-supertraits.rs         |  8 ++-
 src/test/compile-fail/hrtb-just-for-static.rs |  2 +-
 .../compile-fail/hrtb-perfect-forwarding.rs   |  2 +-
 src/test/compile-fail/issue-40000.rs          |  1 -
 src/test/compile-fail/where-for-self-2.rs     |  2 +-
 12 files changed, 81 insertions(+), 14 deletions(-)
 create mode 100644 src/librustc/infer/error_reporting/skol_conflict.rs

diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index e9916bd77e758..ebcd76246223c 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -82,6 +82,7 @@ mod named_anon_conflict;
 #[macro_use]
 mod util;
 mod different_lifetimes;
+mod skol_conflict;
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn note_and_explain_region(self,
@@ -293,7 +294,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             debug!("report_region_errors: error = {:?}", error);
 
             if !self.try_report_named_anon_conflict(&error) &&
-               !self.try_report_anon_anon_conflict(&error) {
+               !self.try_report_anon_anon_conflict(&error) &&
+               !self.try_report_skol_conflict(&error) {
 
                match error.clone() {
                   // These errors could indicate all manner of different
diff --git a/src/librustc/infer/error_reporting/skol_conflict.rs b/src/librustc/infer/error_reporting/skol_conflict.rs
new file mode 100644
index 0000000000000..43aad8eddb91e
--- /dev/null
+++ b/src/librustc/infer/error_reporting/skol_conflict.rs
@@ -0,0 +1,54 @@
+// Copyright 2012-2013 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.
+
+//! Error Reporting for Anonymous Region Lifetime Errors
+//! where both the regions are anonymous.
+use infer::InferCtxt;
+use ty;
+use ty::error::TypeError;
+use infer;
+use infer::region_inference::RegionResolutionError::*;
+use infer::region_inference::RegionResolutionError;
+
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    pub fn try_report_skol_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
+        let (origin, sub, sup) = match *error {
+            ConcreteFailure(ref origin, sub, sup) if sub.is_skolemized() =>
+                (origin, sub, sup),
+            ConcreteFailure(ref origin, sub, sup) if sup.is_skolemized() =>
+                (origin, sub, sup),
+            SubSupConflict(_, ref sub_origin, sub, _, sup) if sub.is_skolemized() =>
+                (sub_origin, sub, sup),
+            SubSupConflict(_, _, sub, ref sup_origin, sup) if sup.is_skolemized() =>
+                (sup_origin, sub, sup),
+            _ =>
+                return false, // inapplicable
+        };
+
+        match *origin {
+            infer::Subtype(ref trace) => {
+                // FIXME This is bogus: we can't really tell from this
+                // information whether it is "overly" polymorphic or what. We
+                // should change how this error is reported altogether. But it
+                // will do for now.
+                let terr = match (sub, sup) {
+                    (&ty::ReSkolemized(_, br_sub), r) =>
+                        TypeError::RegionsInsufficientlyPolymorphic(br_sub, r),
+                    (r, &ty::ReSkolemized(_, br_sup)) =>
+                        TypeError::RegionsInsufficientlyPolymorphic(br_sup, r),
+                    _ => span_bug!(origin.span(), "at least one side must be skolemized")
+                };
+                self.report_and_explain_type_error(trace.clone(), &terr).emit();
+                true
+            }
+            _ => false,
+        }
+    }
+}
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index c1328552ccb4e..d8ec37dfa682e 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -980,6 +980,13 @@ impl RegionKind {
         }
     }
 
+    pub fn is_skolemized(&self) -> bool {
+        match *self {
+            ty::ReSkolemized(..) => true,
+            _ => false,
+        }
+    }
+
     pub fn needs_infer(&self) -> bool {
         match *self {
             ty::ReVar(..) | ty::ReSkolemized(..) => true,
diff --git a/src/test/compile-fail/associated-types-eq-hr.rs b/src/test/compile-fail/associated-types-eq-hr.rs
index ea2c2a23223ee..f456f185e412f 100644
--- a/src/test/compile-fail/associated-types-eq-hr.rs
+++ b/src/test/compile-fail/associated-types-eq-hr.rs
@@ -99,12 +99,14 @@ fn b() {
 
 fn c() {
     tuple_one::<Tuple>();
-    //~^ ERROR type mismatch
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
 }
 
 fn d() {
     tuple_two::<Tuple>();
-    //~^ ERROR type mismatch
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
 }
 
 fn e() {
@@ -113,7 +115,7 @@ fn e() {
 
 fn g() {
     tuple_four::<Tuple>();
-    //~^ ERROR E0277
+    //~^ ERROR mismatched types
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/associated-types/higher-ranked-projection.rs b/src/test/compile-fail/associated-types/higher-ranked-projection.rs
index 12341fa8db38f..07266c44644eb 100644
--- a/src/test/compile-fail/associated-types/higher-ranked-projection.rs
+++ b/src/test/compile-fail/associated-types/higher-ranked-projection.rs
@@ -33,6 +33,5 @@ fn foo<U, T>(_t: T)
 #[rustc_error]
 fn main() { //[good]~ ERROR compilation successful
     foo(());
-    //[bad]~^ ERROR type mismatch resolving `for<'a> <&'a _ as Mirror>::Image == _`
-    //[bad]~| expected bound lifetime parameter 'a, found concrete lifetime
+    //[bad]~^ ERROR mismatched types
 }
diff --git a/src/test/compile-fail/hrtb-conflate-regions.rs b/src/test/compile-fail/hrtb-conflate-regions.rs
index 845429d4b0c0b..33e8dd07a81a6 100644
--- a/src/test/compile-fail/hrtb-conflate-regions.rs
+++ b/src/test/compile-fail/hrtb-conflate-regions.rs
@@ -35,6 +35,6 @@ impl<'a> Foo<(&'a isize, &'a isize)> for SomeStruct
 }
 
 fn a() { want_foo1::<SomeStruct>(); } // OK -- foo wants just one region
-fn b() { want_foo2::<SomeStruct>(); } //~ ERROR E0277
+fn b() { want_foo2::<SomeStruct>(); } //~ ERROR mismatched types
 
 fn main() { }
diff --git a/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs b/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs
index b55dccec2d56f..c38bc23cb86f6 100644
--- a/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs
+++ b/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs
@@ -54,7 +54,7 @@ fn want_qux<B>(b: &B)
     where B : Qux
 {
     want_foo_for_any_tcx(b);
-    want_bar_for_any_ccx(b); //~ ERROR E0277
+    want_bar_for_any_ccx(b); //~ ERROR mismatched types
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs b/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
index 4c5add4aceaaf..6d63f0c15ac42 100644
--- a/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
+++ b/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
@@ -25,7 +25,9 @@ fn want_foo_for_some_tcx<'x,F>(f: &'x F)
     where F : Foo<'x>
 {
     want_foo_for_some_tcx(f);
-    want_foo_for_any_tcx(f); //~ ERROR E0277
+    want_foo_for_any_tcx(f);
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
 }
 
 fn want_foo_for_any_tcx<F>(f: &F)
@@ -42,7 +44,9 @@ fn want_bar_for_some_ccx<'x,B>(b: &B)
     want_foo_for_any_tcx(b);
 
     want_bar_for_some_ccx(b);
-    want_bar_for_any_ccx(b); //~ ERROR E0277
+    want_bar_for_any_ccx(b);
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
 }
 
 fn want_bar_for_any_ccx<B>(b: &B)
diff --git a/src/test/compile-fail/hrtb-just-for-static.rs b/src/test/compile-fail/hrtb-just-for-static.rs
index aec950f992cf4..968f8f3c1a6d2 100644
--- a/src/test/compile-fail/hrtb-just-for-static.rs
+++ b/src/test/compile-fail/hrtb-just-for-static.rs
@@ -31,7 +31,7 @@ fn give_any() {
 struct StaticInt;
 impl Foo<&'static isize> for StaticInt { }
 fn give_static() {
-    want_hrtb::<StaticInt>() //~ ERROR `for<'a> StaticInt: Foo<&'a isize>` is not satisfied
+    want_hrtb::<StaticInt>() //~ ERROR mismatched types
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/hrtb-perfect-forwarding.rs b/src/test/compile-fail/hrtb-perfect-forwarding.rs
index fcfbeefced06b..ef9a5046271ed 100644
--- a/src/test/compile-fail/hrtb-perfect-forwarding.rs
+++ b/src/test/compile-fail/hrtb-perfect-forwarding.rs
@@ -53,7 +53,7 @@ fn foo_hrtb_bar_not<'b,T>(mut t: T)
     // be implemented. Thus to satisfy `&mut T : for<'a> Foo<&'a
     // isize>`, we require `T : for<'a> Bar<&'a isize>`, but the where
     // clause only specifies `T : Bar<&'b isize>`.
-    foo_hrtb_bar_not(&mut t); //~ ERROR `for<'a> T: Bar<&'a isize>` is not satisfied
+    foo_hrtb_bar_not(&mut t); //~ ERROR mismatched types
 }
 
 fn foo_hrtb_bar_hrtb<T>(mut t: T)
diff --git a/src/test/compile-fail/issue-40000.rs b/src/test/compile-fail/issue-40000.rs
index 7daf4bcbaa44b..be0ccae89aa16 100644
--- a/src/test/compile-fail/issue-40000.rs
+++ b/src/test/compile-fail/issue-40000.rs
@@ -14,5 +14,4 @@ fn main() {
     fn foo(x: Box<Fn(&i32)>) {}
     let bar = Box::new(|x: &i32| {}) as Box<Fn(_)>;
     foo(bar); //~ ERROR mismatched types
-    //~| expected concrete lifetime, found bound lifetime parameter
 }
diff --git a/src/test/compile-fail/where-for-self-2.rs b/src/test/compile-fail/where-for-self-2.rs
index bf8fc29217338..8092ae87cd663 100644
--- a/src/test/compile-fail/where-for-self-2.rs
+++ b/src/test/compile-fail/where-for-self-2.rs
@@ -29,5 +29,5 @@ fn foo<T>(x: &T)
 
 fn main() {
     foo(&X);
-    //~^ error: `for<'a> &'a _: Bar` is not satisfied
+    //~^ ERROR mismatched types
 }

From 87cd56e6003deba11c163984e016c9456389cb40 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 2 Sep 2017 05:25:56 -0400
Subject: [PATCH 34/63] introduce `instantiable_as`

---
 src/librustc/infer/at.rs       | 30 ++++++++++++++++++++++++++++++
 src/librustc/traits/project.rs |  7 ++-----
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs
index 44951f0f915fb..b29a390485c26 100644
--- a/src/librustc/infer/at.rs
+++ b/src/librustc/infer/at.rs
@@ -111,6 +111,36 @@ impl<'a, 'gcx, 'tcx> At<'a, 'gcx, 'tcx> {
         self.sub_exp(false, actual, expected)
     }
 
+    /// See if we can instantiate `expected` in such a way that
+    /// `actual` would be an instance of it. Commonly used in trait
+    /// matching, where you might have a rule like:
+    ///
+    ///     expected = for<'a> T: Foo<'a>
+    ///
+    /// and we want to see if it can full `actual == T: Foo<'static>`.
+    ///
+    /// This routine would then instantiate `'a` with a fresh
+    /// inference variable and equate the result with `actual`.
+    pub fn instantiable_as<T>(self,
+                              expected: ty::Binder<T>,
+                              actual: T)
+                              -> InferResult<'tcx, ()>
+        where T: ToTrace<'tcx>
+    {
+        debug!("instantiable_as(expected={:?}, actual={:?})", expected, actual);
+
+        let (expected_prime, _) =
+            self.infcx.replace_late_bound_regions_with_fresh_var(
+                self.cause.span,
+                self.param_env.universe,
+                HigherRankedType,
+                &expected);
+
+        debug!("instantiable_as: expected_prime={:?}", expected_prime);
+
+        self.eq(expected_prime, actual)
+    }
+
     /// Make `expected <: actual`
     pub fn sub<T>(self,
                   expected: T,
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 96b908da4be95..6f6a4aa852b73 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -953,10 +953,8 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
                 let is_match = same_def_id && infcx.probe(|_| {
                     let data_poly_trait_ref =
                         data.to_poly_trait_ref(infcx.tcx);
-                    let obligation_poly_trait_ref =
-                        obligation_trait_ref.to_poly_trait_ref();
                     infcx.at(&obligation.cause, obligation.param_env)
-                         .sup(obligation_poly_trait_ref, data_poly_trait_ref)
+                         .instantiable_as(data_poly_trait_ref, *obligation_trait_ref)
                          .map(|InferOk { obligations: _, value: () }| {
                              // FIXME(#32730) -- do we need to take obligations
                              // into account in any way? At the moment, no.
@@ -1222,10 +1220,9 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
         // select those with a relevant trait-ref
         let mut env_predicates = env_predicates.filter(|data| {
             let data_poly_trait_ref = data.to_poly_trait_ref(selcx.tcx());
-            let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
             selcx.infcx().probe(|_| {
                 selcx.infcx().at(&obligation.cause, obligation.param_env)
-                             .sup(obligation_poly_trait_ref, data_poly_trait_ref)
+                             .instantiable_as(data_poly_trait_ref, *obligation_trait_ref)
                              .is_ok()
             })
         });

From 5986318e547dee56fa87265c953b4071d2835e36 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 2 Sep 2017 05:26:26 -0400
Subject: [PATCH 35/63] stop passing around `&ty::TraitRef`

---
 src/librustc/traits/project.rs | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 6f6a4aa852b73..edda2e2128ddb 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -770,7 +770,7 @@ fn project_type<'cx, 'gcx, 'tcx>(
         selcx.infcx().report_overflow_error(&obligation, true);
     }
 
-    let obligation_trait_ref = &obligation.predicate.trait_ref(selcx.tcx());
+    let obligation_trait_ref = obligation.predicate.trait_ref(selcx.tcx());
 
     debug!("project: obligation_trait_ref={:?}", obligation_trait_ref);
 
@@ -785,17 +785,17 @@ fn project_type<'cx, 'gcx, 'tcx>(
 
     assemble_candidates_from_param_env(selcx,
                                        obligation,
-                                       &obligation_trait_ref,
+                                       obligation_trait_ref,
                                        &mut candidates);
 
     assemble_candidates_from_trait_def(selcx,
                                        obligation,
-                                       &obligation_trait_ref,
+                                       obligation_trait_ref,
                                        &mut candidates);
 
     if let Err(e) = assemble_candidates_from_impls(selcx,
                                                    obligation,
-                                                   &obligation_trait_ref,
+                                                   obligation_trait_ref,
                                                    &mut candidates) {
         return Err(ProjectionTyError::TraitSelectionError(e));
     }
@@ -856,7 +856,7 @@ fn project_type<'cx, 'gcx, 'tcx>(
             Ok(ProjectedTy::Progress(
                 confirm_candidate(selcx,
                                   obligation,
-                                  &obligation_trait_ref,
+                                  obligation_trait_ref,
                                   candidate)))
         }
         None => Ok(ProjectedTy::NoProgress(
@@ -872,7 +872,7 @@ fn project_type<'cx, 'gcx, 'tcx>(
 fn assemble_candidates_from_param_env<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: &ty::TraitRef<'tcx>,
+    obligation_trait_ref: ty::TraitRef<'tcx>,
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
 {
     debug!("assemble_candidates_from_param_env(..)");
@@ -897,7 +897,7 @@ fn assemble_candidates_from_param_env<'cx, 'gcx, 'tcx>(
 fn assemble_candidates_from_trait_def<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: &ty::TraitRef<'tcx>,
+    obligation_trait_ref: ty::TraitRef<'tcx>,
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
 {
     debug!("assemble_candidates_from_trait_def(..)");
@@ -933,7 +933,7 @@ fn assemble_candidates_from_trait_def<'cx, 'gcx, 'tcx>(
 fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: &ty::TraitRef<'tcx>,
+    obligation_trait_ref: ty::TraitRef<'tcx>,
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
     ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>,
     env_predicates: I)
@@ -954,7 +954,7 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
                     let data_poly_trait_ref =
                         data.to_poly_trait_ref(infcx.tcx);
                     infcx.at(&obligation.cause, obligation.param_env)
-                         .instantiable_as(data_poly_trait_ref, *obligation_trait_ref)
+                         .instantiable_as(data_poly_trait_ref, obligation_trait_ref)
                          .map(|InferOk { obligations: _, value: () }| {
                              // FIXME(#32730) -- do we need to take obligations
                              // into account in any way? At the moment, no.
@@ -978,7 +978,7 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
 fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: &ty::TraitRef<'tcx>,
+    obligation_trait_ref: ty::TraitRef<'tcx>,
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
     -> Result<(), SelectionError<'tcx>>
 {
@@ -1120,7 +1120,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
 fn confirm_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: &ty::TraitRef<'tcx>,
+    obligation_trait_ref: ty::TraitRef<'tcx>,
     candidate: ProjectionTyCandidate<'tcx>)
     -> Progress<'tcx>
 {
@@ -1143,7 +1143,7 @@ fn confirm_candidate<'cx, 'gcx, 'tcx>(
 fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: &ty::TraitRef<'tcx>)
+    obligation_trait_ref: ty::TraitRef<'tcx>)
     -> Progress<'tcx>
 {
     let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
@@ -1183,7 +1183,7 @@ fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
 fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation:  &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: &ty::TraitRef<'tcx>)
+    obligation_trait_ref: ty::TraitRef<'tcx>)
     -> Progress<'tcx>
 {
     let self_ty = obligation_trait_ref.self_ty();
@@ -1222,7 +1222,7 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
             let data_poly_trait_ref = data.to_poly_trait_ref(selcx.tcx());
             selcx.infcx().probe(|_| {
                 selcx.infcx().at(&obligation.cause, obligation.param_env)
-                             .instantiable_as(data_poly_trait_ref, *obligation_trait_ref)
+                             .instantiable_as(data_poly_trait_ref, obligation_trait_ref)
                              .is_ok()
             })
         });

From 96126ab6eaef85e4473f4735c06c2836d09a8b4e Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 2 Sep 2017 05:29:21 -0400
Subject: [PATCH 36/63] simplify a few things in `select.rs`

---
 src/librustc/traits/select.rs | 29 +++++++++--------------------
 1 file changed, 9 insertions(+), 20 deletions(-)

diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 57c1a91f7c34a..473e69263b75a 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -47,7 +47,6 @@ use std::cell::RefCell;
 use std::cmp;
 use std::fmt;
 use std::marker::PhantomData;
-use std::mem;
 use std::rc::Rc;
 use syntax::abi::Abi;
 use hir;
@@ -1657,14 +1656,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             obligation.predicate.def_id(),
             obligation.predicate.0.trait_ref.self_ty(),
             |impl_def_id| {
-                self.probe(|this, _snapshot| { /* [1] */
+                self.probe(|this, _snapshot| {
                     match this.match_impl(impl_def_id, obligation) {
-                        Ok(skol_map) => {
+                        Ok(_) => {
                             candidates.vec.push(ImplCandidate(impl_def_id));
-
-                            // NB: we can safely drop the skol map
-                            // since we are in a probe [1]
-                            mem::drop(skol_map);
                         }
                         Err(_) => { }
                     }
@@ -1744,7 +1739,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // self-type from one of the other inputs. Without this check,
         // these cases wind up being considered ambiguous due to a
         // (spurious) ambiguity introduced here.
-        let predicate_trait_ref = obligation.predicate.to_poly_trait_ref();
+        let predicate_trait_ref = obligation.predicate;
         if !self.tcx().is_object_safe(predicate_trait_ref.def_id()) {
             return;
         }
@@ -2471,7 +2466,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // First, create the substitutions by matching the impl again,
         // this time not in a probe.
         self.in_snapshot(|this, _snapshot| {
-            let (substs, param_env, _skol_map) =
+            let (substs, param_env) =
                 this.rematch_impl(impl_def_id, obligation);
             debug!("confirm_impl_candidate substs={:?}", substs);
             let cause = obligation.derived_cause(ImplDerivedObligation);
@@ -2975,8 +2970,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     impl_def_id: DefId,
                     obligation: &TraitObligation<'tcx>)
                     -> (Normalized<'tcx, &'tcx Substs<'tcx>>,
-                        ty::ParamEnv<'tcx>,
-                        infer::SkolemizationMap<'tcx>)
+                        ty::ParamEnv<'tcx>)
     {
         match self.match_impl(impl_def_id, obligation) {
             Ok(tuple) => tuple,
@@ -2992,8 +2986,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                   impl_def_id: DefId,
                   obligation: &TraitObligation<'tcx>)
                   -> Result<(Normalized<'tcx, &'tcx Substs<'tcx>>,
-                             ty::ParamEnv<'tcx>,
-                             infer::SkolemizationMap<'tcx>), ()>
+                             ty::ParamEnv<'tcx>), ()>
     {
         let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
 
@@ -3004,7 +2997,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             return Err(());
         }
 
-        let (skol_obligation, param_env, skol_map) =
+        let (skol_obligation, param_env, _skol_map) =
              self.infcx().skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
         let skol_obligation_trait_ref = skol_obligation.trait_ref;
 
@@ -3039,12 +3032,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         self.inferred_obligations.extend(obligations);
 
         debug!("match_impl: success impl_substs={:?}", impl_substs);
-        Ok((Normalized {
-            value: impl_substs,
-            obligations: impl_trait_ref.obligations
-        },
-            param_env,
-            skol_map))
+        Ok((Normalized { value: impl_substs, obligations: impl_trait_ref.obligations },
+            param_env))
     }
 
     fn fast_reject_trait_refs(&mut self,

From e70a304fe91a577a1b8b32fa37bfbc36778e38f7 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 3 Sep 2017 05:06:48 -0400
Subject: [PATCH 37/63] remove `TraitPredicate` and just use `TraitRef`

I think time has shown that this is not a useful newtype. The only
handy thing it did was help with the Display impl.
---
 src/librustc/ich/impls_ty.rs                  |  1 -
 src/librustc/infer/error_reporting/mod.rs     | 12 +++-
 src/librustc/infer/mod.rs                     | 11 ++-
 src/librustc/traits/error_reporting.rs        | 25 +++----
 src/librustc/traits/fulfill.rs                | 19 +++--
 src/librustc/traits/mod.rs                    |  2 +-
 src/librustc/traits/project.rs                |  4 +-
 src/librustc/traits/select.rs                 | 70 +++++++++----------
 src/librustc/traits/specialize/mod.rs         |  2 +-
 .../traits/specialize/specialization_graph.rs |  2 +-
 src/librustc/traits/trans/mod.rs              |  4 +-
 src/librustc/traits/util.rs                   |  6 +-
 src/librustc/ty/item_path.rs                  |  4 +-
 src/librustc/ty/mod.rs                        | 51 ++------------
 src/librustc/ty/structural_impls.rs           | 60 ++++++++++------
 src/librustc/ty/sty.rs                        | 54 +++++++++++---
 src/librustc/ty/wf.rs                         |  2 +-
 src/librustc/util/ppaux.rs                    | 64 ++++++++++-------
 src/librustc_lint/builtin.rs                  |  2 +-
 src/librustc_privacy/lib.rs                   | 14 ++--
 src/librustc_typeck/astconv.rs                |  6 +-
 src/librustc_typeck/check/closure.rs          |  4 +-
 src/librustc_typeck/check/method/probe.rs     | 13 ++--
 src/librustc_typeck/check/method/suggest.rs   |  2 +-
 src/librustc_typeck/check/mod.rs              |  6 +-
 src/librustc_typeck/coherence/overlap.rs      |  2 +-
 src/librustc_typeck/coherence/unsafety.rs     |  4 +-
 src/librustdoc/clean/mod.rs                   |  9 ---
 28 files changed, 238 insertions(+), 217 deletions(-)

diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index c8000968e61e4..a676953f12422 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -184,7 +184,6 @@ impl_stable_hash_for!(enum ty::Visibility {
 });
 
 impl_stable_hash_for!(struct ty::TraitRef<'tcx> { def_id, substs });
-impl_stable_hash_for!(struct ty::TraitPredicate<'tcx> { trait_ref });
 impl_stable_hash_for!(tuple_struct ty::EquatePredicate<'tcx> { t1, t2 });
 impl_stable_hash_for!(struct ty::SubtypePredicate<'tcx> { a_is_expected, a, b });
 
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index ebcd76246223c..486c4dcc99709 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -797,9 +797,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         -> Option<(DiagnosticStyledString, DiagnosticStyledString)>
     {
         match *values {
-            infer::Types(ref exp_found) => self.expected_found_str_ty(exp_found),
-            infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found),
-            infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found),
+            infer::Types(ref exp_found) => {
+                self.expected_found_str_ty(exp_found)
+            }
+            infer::TraitRefs(ref exp_found) => {
+                self.expected_found_str(&exp_found.map(|t| t.print_with_colon()))
+            }
+            infer::PolyTraitRefs(ref exp_found) => {
+                self.expected_found_str(&exp_found.map(|t| t.print_with_colon()))
+            }
         }
     }
 
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index c310b9f22b7ab..abe8a35b32928 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -397,6 +397,15 @@ impl<T> ExpectedFound<T> {
             ExpectedFound {expected: b, found: a}
         }
     }
+
+    pub fn map<OP, R>(self, mut op: OP) -> ExpectedFound<R>
+        where OP: FnMut(T) -> R
+    {
+        ExpectedFound {
+            expected: op(self.expected),
+            found: op(self.found),
+        }
+    }
 }
 
 impl<'tcx, T> InferOk<'tcx, T> {
@@ -1078,7 +1087,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 
     pub fn trait_ref_to_string(&self, t: &ty::TraitRef<'tcx>) -> String {
-        self.resolve_type_vars_if_possible(t).to_string()
+        self.resolve_type_vars_if_possible(t).print_without_self().to_string()
     }
 
     pub fn shallow_resolve(&self, typ: Ty<'tcx>) -> Ty<'tcx> {
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 4a6305e9bf807..7e69772187f16 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -36,7 +36,7 @@ use middle::const_val;
 use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
 use std::fmt;
 use syntax::ast;
-use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
+use ty::{self, AdtKind, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use ty::error::ExpectedFound;
 use ty::fast_reject;
 use ty::fold::TypeFolder;
@@ -126,7 +126,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
 
         let (cond, error) = match (cond, error) {
-            (&ty::Predicate::Trait(..), &ty::Predicate::Trait(ref error))
+            (&ty::Predicate::Trait(..), &ty::Predicate::Trait(error))
                 => (cond, error),
             _ => {
                 // FIXME: make this work in other cases too.
@@ -136,8 +136,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         for implication in super::elaborate_predicates(self.tcx, vec![cond.clone()]) {
             if let ty::Predicate::Trait(implication) = implication {
-                let error = error.to_poly_trait_ref();
-                let implication = implication.to_poly_trait_ref();
                 // FIXME: I'm just not taking associated types at all here.
                 // Eventually I'll need to implement param-env-aware
                 // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic.
@@ -552,14 +550,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     return;
                 }
                 match obligation.predicate {
-                    ty::Predicate::Trait(ref trait_predicate) => {
-                        let trait_predicate =
-                            self.resolve_type_vars_if_possible(trait_predicate);
+                    ty::Predicate::Trait(ref trait_ref) => {
+                        let trait_ref = self.resolve_type_vars_if_possible(trait_ref);
 
-                        if self.tcx.sess.has_errors() && trait_predicate.references_error() {
+                        if self.tcx.sess.has_errors() && trait_ref.references_error() {
                             return;
                         }
-                        let trait_ref = trait_predicate.to_poly_trait_ref();
                         let (post_message, pre_message) =
                             self.get_parent_trait_ref(&obligation.cause.code)
                                 .map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t)))
@@ -585,13 +581,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                             err.span_label(span, s.as_str());
                             err.help(&format!("{}the trait `{}` is not implemented for `{}`",
                                               pre_message,
-                                              trait_ref,
+                                              trait_ref.print_without_self(),
                                               trait_ref.self_ty()));
                         } else {
                             err.span_label(span,
                                            &*format!("{}the trait `{}` is not implemented for `{}`",
                                                      pre_message,
-                                                     trait_ref,
+                                                     trait_ref.print_without_self(),
                                                      trait_ref.self_ty()));
                         }
 
@@ -606,7 +602,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                             //     "the type `T` can't be frobnicated"
                             // which is somewhat confusing.
                             err.help(&format!("consider adding a `where {}` bound",
-                                                trait_ref.to_predicate()));
+                                              trait_ref.to_predicate()));
                         } else if !have_alt_message {
                             // Can't show anything else useful, try to find similar impls.
                             let impl_candidates = self.find_similar_impl_candidates(trait_ref);
@@ -1047,8 +1043,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
 
         match predicate {
-            ty::Predicate::Trait(ref data) => {
-                let trait_ref = data.to_poly_trait_ref();
+            ty::Predicate::Trait(trait_ref) => {
                 let self_ty = trait_ref.self_ty();
                 if predicate.references_error() {
                     return;
@@ -1298,7 +1293,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 let parent_trait_ref = self.resolve_type_vars_if_possible(&data.parent_trait_ref);
                 err.note(
                     &format!("required because of the requirements on the impl of `{}` for `{}`",
-                             parent_trait_ref,
+                             parent_trait_ref.print_without_self(),
                              parent_trait_ref.0.self_ty()));
                 let parent_predicate = parent_trait_ref.to_predicate();
                 self.note_obligation_cause_code(err,
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index cd48417e4a2da..3e9f8f67ca444 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use infer::{InferCtxt, InferOk};
-use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate};
+use ty::{self, Ty, TypeFoldable, ToPredicate};
 use ty::error::ExpectedFound;
 use rustc_data_structures::obligation_forest::{ObligationForest, Error};
 use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor};
@@ -357,10 +357,10 @@ fn process_predicate<'a, 'gcx, 'tcx>(
     }
 
     match obligation.predicate {
-        ty::Predicate::Trait(ref data) => {
-            let trait_obligation = obligation.with(data.clone());
+        ty::Predicate::Trait(trait_ref) => {
+            let trait_obligation = obligation.with(trait_ref.clone());
 
-            if data.is_global() {
+            if trait_ref.is_global() {
                 // no type variables present, can use evaluation for better caching.
                 // FIXME: consider caching errors too.
                 if
@@ -369,7 +369,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
                     !trait_obligation.predicate.skip_binder().self_ty().is_defaulted_unit() &&
                     selcx.evaluate_obligation_conservatively(&obligation) {
                     debug!("selecting trait `{:?}` at depth {} evaluated to holds",
-                           data, obligation.recursion_depth);
+                           trait_ref, obligation.recursion_depth);
                     return Ok(Some(vec![]))
                 }
             }
@@ -377,12 +377,12 @@ fn process_predicate<'a, 'gcx, 'tcx>(
             match selcx.select(&trait_obligation) {
                 Ok(Some(vtable)) => {
                     debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
-                           data, obligation.recursion_depth);
+                           trait_ref, obligation.recursion_depth);
                     Ok(Some(vtable.nested_obligations()))
                 }
                 Ok(None) => {
                     debug!("selecting trait `{:?}` at depth {} yielded Ok(None)",
-                           data, obligation.recursion_depth);
+                           trait_ref, obligation.recursion_depth);
 
                     // This is a bit subtle: for the most part, the
                     // only reason we can fail to make progress on
@@ -397,8 +397,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
                     // the same time.
                     //
                     // FIXME(#32286) logic seems false if no upvars
-                    pending_obligation.stalled_on =
-                        trait_ref_type_vars(selcx, data.to_poly_trait_ref());
+                    pending_obligation.stalled_on = trait_ref_type_vars(selcx, trait_ref);
 
                     debug!("process_predicate: pending obligation {:?} now stalled on {:?}",
                            selcx.infcx().resolve_type_vars_if_possible(obligation),
@@ -408,7 +407,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
                 }
                 Err(selection_err) => {
                     info!("selecting trait `{:?}` at depth {} yielded Err",
-                          data, obligation.recursion_depth);
+                          trait_ref, obligation.recursion_depth);
 
                     Err(CodeSelectionError(selection_err))
                 }
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index b464f0ca06145..183226e985a80 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -86,7 +86,7 @@ pub struct Obligation<'tcx, T> {
 }
 
 pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
-pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
+pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitRef<'tcx>>;
 
 /// Why did we incur this obligation? Used for error reporting.
 #[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index edda2e2128ddb..6d5d259dbd2b8 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -985,7 +985,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
     // If we are resolving `<T as TraitRef<...>>::Item == Type`,
     // start out by selecting the predicate `T as TraitRef<...>`:
     let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
-    let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate());
+    let trait_obligation = obligation.with(poly_trait_ref);
     selcx.infcx().probe(|_| {
         let vtable = match selcx.select(&trait_obligation) {
             Ok(Some(vtable)) => vtable,
@@ -1147,7 +1147,7 @@ fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
     -> Progress<'tcx>
 {
     let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
-    let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate());
+    let trait_obligation = obligation.with(poly_trait_ref);
     let vtable = match selcx.select(&trait_obligation) {
         Ok(Some(vtable)) => vtable,
         _ => {
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 473e69263b75a..753c5ecfb5941 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -35,7 +35,7 @@ use hir::def_id::DefId;
 use infer;
 use infer::{InferCtxt, InferOk, TypeFreshener};
 use ty::subst::{Kind, Subst, Substs};
-use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
+use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use ty::fast_reject;
 use ty::relate::TypeRelation;
 use middle::lang_items;
@@ -534,18 +534,17 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // a switch can't cause code to stop compiling or execute
             // differently.
             let mut never_obligation = obligation.clone();
-            let def_id = never_obligation.predicate.skip_binder().trait_ref.def_id;
-            never_obligation.predicate = never_obligation.predicate.map_bound(|mut trait_pred| {
+            let def_id = never_obligation.predicate.def_id();
+            never_obligation.predicate = never_obligation.predicate.map_bound(|mut trait_ref| {
                 // Swap out () with ! so we can check if the trait is impld for !
                 {
-                    let trait_ref = &mut trait_pred.trait_ref;
                     let unit_substs = trait_ref.substs;
                     let mut never_substs = Vec::with_capacity(unit_substs.len());
                     never_substs.push(From::from(tcx.types.never));
                     never_substs.extend(&unit_substs[1..]);
                     trait_ref.substs = tcx.intern_substs(&never_substs);
                 }
-                trait_pred
+                trait_ref
             });
             if let Ok(Some(..)) = self.select(&never_obligation) {
                 if !tcx.trait_relevant_for_never(def_id) {
@@ -819,10 +818,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // Heuristics: show the diagnostics when there are no candidates in crate.
             if let Ok(candidate_set) = self.assemble_candidates(stack) {
                 if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
-                    let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
+                    let trait_ref = stack.obligation.predicate.skip_binder();
                     let self_ty = trait_ref.self_ty();
                     let cause = IntercrateAmbiguityCause::DownstreamCrate {
-                        trait_desc: trait_ref.to_string(),
+                        trait_desc: trait_ref.print_with_colon().to_string(),
                         self_desc: if self_ty.has_concrete_skeleton() {
                             Some(self_ty.to_string())
                         } else {
@@ -1004,7 +1003,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // not update) the cache.
         let recursion_limit = self.infcx.tcx.sess.recursion_limit.get();
         if stack.obligation.recursion_depth >= recursion_limit {
-            self.infcx().report_overflow_error(&stack.obligation, true);
+            let predicate = stack.obligation.predicate.print_with_colon();
+            let obligation = stack.obligation.with(predicate);
+            self.infcx().report_overflow_error(&obligation, true);
         }
 
         // Check the cache. Note that we skolemize the trait-ref
@@ -1081,9 +1082,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // Heuristics: show the diagnostics when there are no candidates in crate.
             let candidate_set = self.assemble_candidates(stack)?;
             if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
-                let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
+                let trait_ref = *stack.obligation.predicate.skip_binder();
                 let self_ty = trait_ref.self_ty();
-                let trait_desc = trait_ref.to_string();
+                let trait_desc = trait_ref.print_without_self().to_string();
                 let self_desc = if self_ty.has_concrete_skeleton() {
                     Some(self_ty.to_string())
                 } else {
@@ -1218,7 +1219,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // ok to skip binder because of the nature of the
         // trait-ref-is-knowable check, which does not care about
         // bound regions
-        let trait_ref = predicate.skip_binder().trait_ref;
+        let trait_ref = *predicate.skip_binder();
 
         coherence::trait_ref_is_knowable(self.tcx(), trait_ref)
     }
@@ -1255,11 +1256,11 @@ 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>)
+                             cache_fresh_trait_pred: &ty::PolyTraitRef<'tcx>)
                              -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>>
     {
         let tcx = self.tcx();
-        let trait_ref = &cache_fresh_trait_pred.0.trait_ref;
+        let trait_ref = cache_fresh_trait_pred.skip_binder();
         if self.can_use_global_caches(param_env) {
             let cache = tcx.selection_cache.hashmap.borrow();
             if let Some(cached) = cache.get(&trait_ref) {
@@ -1274,12 +1275,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>,
+                              cache_fresh_trait_pred: ty::PolyTraitRef<'tcx>,
                               dep_node: DepNodeIndex,
                               candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>)
     {
         let tcx = self.tcx();
-        let trait_ref = cache_fresh_trait_pred.0.trait_ref;
+        let trait_ref = *cache_fresh_trait_pred.skip_binder();
         if self.can_use_global_caches(param_env) {
             let mut cache = tcx.selection_cache.hashmap.borrow_mut();
             if let Some(trait_ref) = tcx.lift_to_global(&trait_ref) {
@@ -1384,7 +1385,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         // before we go into the whole skolemization thing, just
         // quickly check if the self-type is a projection at all.
-        match obligation.predicate.0.trait_ref.self_ty().sty {
+        match obligation.predicate.skip_binder().self_ty().sty {
             ty::TyProjection(_) | ty::TyAnon(..) => {}
             ty::TyInfer(ty::TyVar(_)) => {
                 span_bug!(obligation.cause.span,
@@ -1417,7 +1418,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let obligation = obligation.clone().with_env(param_env);
 
-        let (def_id, substs) = match skol_trait_predicate.trait_ref.self_ty().sty {
+        let (def_id, substs) = match skol_trait_predicate.self_ty().sty {
             ty::TyProjection(ref data) =>
                 (data.trait_ref(self.tcx()).def_id, data.substs),
             ty::TyAnon(def_id, substs) => (def_id, substs),
@@ -1426,7 +1427,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     obligation.cause.span,
                     "match_projection_obligation_against_definition_bounds() called \
                      but self-ty not a projection: {:?}",
-                    skol_trait_predicate.trait_ref.self_ty());
+                    skol_trait_predicate.self_ty());
             }
         };
         debug!("match_projection_obligation_against_definition_bounds: \
@@ -1446,7 +1447,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 |bound| self.probe(
                     |this, _| this.match_projection(&obligation,
                                                     bound.clone(),
-                                                    skol_trait_predicate.trait_ref.clone())));
+                                                    skol_trait_predicate)));
 
         debug!("match_projection_obligation_against_definition_bounds: \
                 matching_bound={:?}",
@@ -1457,7 +1458,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 // Repeat the successful match, if any, this time outside of a probe.
                 let result = self.match_projection(&obligation,
                                                    bound,
-                                                   skol_trait_predicate.trait_ref.clone());
+                                                   skol_trait_predicate);
                 assert!(result);
                 true
             }
@@ -1572,7 +1573,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                    candidates: &mut SelectionCandidateSet<'tcx>)
                                    -> Result<(),SelectionError<'tcx>>
     {
-        let kind = match self.tcx().lang_items().fn_trait_kind(obligation.predicate.0.def_id()) {
+        let kind = match self.tcx().lang_items().fn_trait_kind(obligation.predicate.def_id()) {
             Some(k) => k,
             None => { return Ok(()); }
         };
@@ -1654,7 +1655,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         self.tcx().for_each_relevant_impl(
             obligation.predicate.def_id(),
-            obligation.predicate.0.trait_ref.self_ty(),
+            obligation.predicate.skip_binder().self_ty(),
             |impl_def_id| {
                 self.probe(|this, _snapshot| {
                     match this.match_impl(impl_def_id, obligation) {
@@ -1825,7 +1826,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 return;
             }
         };
-        let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
+        let target = obligation.predicate.skip_binder().substs.type_at(1);
 
         debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})",
                source, target);
@@ -2433,7 +2434,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             nested);
 
         let trait_obligations = self.in_snapshot(|this, _snapshot| {
-            let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
+            let poly_trait_ref = obligation.predicate;
             let (trait_ref, param_env, _skol_map) =
                 this.infcx().skolemize_late_bound_regions(obligation.param_env, &poly_trait_ref);
             let cause = obligation.derived_cause(ImplDerivedObligation);
@@ -2602,7 +2603,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         self.confirm_poly_trait_refs(obligation.cause.clone(),
                                      obligation.param_env,
-                                     obligation.predicate.to_poly_trait_ref(),
+                                     obligation.predicate,
                                      trait_ref)?;
         Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations })
     }
@@ -2644,7 +2645,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         self.confirm_poly_trait_refs(obligation.cause.clone(),
                                      obligation.param_env,
-                                     obligation.predicate.to_poly_trait_ref(),
+                                     obligation.predicate,
                                      trait_ref)?;
 
         Ok(VtableGeneratorData {
@@ -2661,7 +2662,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     {
         debug!("confirm_closure_candidate({:?})", obligation);
 
-        let kind = match self.tcx().lang_items().fn_trait_kind(obligation.predicate.0.def_id()) {
+        let kind = match self.tcx().lang_items().fn_trait_kind(obligation.predicate.def_id()) {
             Some(k) => k,
             None => bug!("closure candidate for non-fn trait {:?}", obligation)
         };
@@ -2693,7 +2694,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         self.confirm_poly_trait_refs(obligation.cause.clone(),
                                      obligation.param_env,
-                                     obligation.predicate.to_poly_trait_ref(),
+                                     obligation.predicate,
                                      trait_ref)?;
 
         obligations.push(Obligation::new(
@@ -2758,7 +2759,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // regions here. See the comment there for more details.
         let source = self.infcx.shallow_resolve(
             tcx.no_late_bound_regions(&obligation.self_ty()).unwrap());
-        let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
+        let target = obligation.predicate.skip_binder().substs.type_at(1);
         let target = self.infcx.shallow_resolve(target);
 
         debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})",
@@ -2997,9 +2998,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             return Err(());
         }
 
-        let (skol_obligation, param_env, _skol_map) =
+        let (skol_obligation_trait_ref, param_env, _skol_map) =
              self.infcx().skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
-        let skol_obligation_trait_ref = skol_obligation.trait_ref;
 
         let impl_substs = self.infcx.fresh_substs_for_item(param_env.universe,
                                                            obligation.cause.span,
@@ -3085,7 +3085,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                poly_trait_ref);
 
         self.infcx.at(&obligation.cause, obligation.param_env)
-                  .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref)
+                  .sup(obligation.predicate, poly_trait_ref)
                   .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
                   .map_err(|_| ())
     }
@@ -3107,9 +3107,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                             obligation: &'o TraitObligation<'tcx>)
                             -> TraitObligationStack<'o, 'tcx>
     {
-        let fresh_trait_ref =
-            obligation.predicate.to_poly_trait_ref().fold_with(&mut self.freshener);
-
+        let fresh_trait_ref = obligation.predicate.fold_with(&mut self.freshener);
         TraitObligationStack {
             obligation,
             fresh_trait_ref,
@@ -3228,7 +3226,7 @@ impl<'tcx> TraitObligation<'tcx> {
         // by using -Z verbose or just a CLI argument.
         if obligation.recursion_depth >= 0 {
             let derived_cause = DerivedObligationCause {
-                parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
+                parent_trait_ref: obligation.predicate,
                 parent_code: Rc::new(obligation.cause.code.clone())
             };
             let derived_code = variant(derived_cause);
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index 5f2136ef9c8cf..685252764f0cc 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -386,7 +386,7 @@ fn to_pretty_impl_header(tcx: TyCtxt, impl_def_id: DefId) -> Option<String> {
         w.push('>');
     }
 
-    write!(w, " {} for {}", trait_ref, tcx.type_of(impl_def_id)).unwrap();
+    write!(w, " {} for {}", trait_ref.print_without_self(), tcx.type_of(impl_def_id)).unwrap();
 
     // The predicates will contain default bounds like `T: Sized`. We need to
     // remove these bounds, and add `T: ?Sized` to any untouched type parameters.
diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs
index da9dbc0e2c999..84728589a7b52 100644
--- a/src/librustc/traits/specialize/specialization_graph.rs
+++ b/src/librustc/traits/specialize/specialization_graph.rs
@@ -130,7 +130,7 @@ impl<'a, 'gcx, 'tcx> Children {
                         let self_ty = trait_ref.self_ty();
                         Err(OverlapError {
                             with_impl: possible_sibling,
-                            trait_desc: trait_ref.to_string(),
+                            trait_desc: trait_ref.print_without_self().to_string(),
                             // only report the Self type if it has at least
                             // some outer concrete shell; otherwise, it's
                             // not adding much information.
diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs
index 73fdbfe8831e3..f6db4420090b8 100644
--- a/src/librustc/traits/trans/mod.rs
+++ b/src/librustc/traits/trans/mod.rs
@@ -45,9 +45,7 @@ pub fn trans_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>,
         let mut selcx = SelectionContext::new(&infcx);
 
         let obligation_cause = ObligationCause::dummy();
-        let obligation = Obligation::new(obligation_cause,
-                                            param_env,
-                                            trait_ref.to_poly_trait_predicate());
+        let obligation = Obligation::new(obligation_cause, param_env, trait_ref);
 
         let selection = match selcx.select(&obligation) {
             Ok(Some(selection)) => selection,
diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs
index 42e0834e8e43b..49e11c4f28f55 100644
--- a/src/librustc/traits/util.rs
+++ b/src/librustc/traits/util.rs
@@ -10,7 +10,7 @@
 
 use hir::def_id::DefId;
 use ty::subst::{Subst, Substs};
-use ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef};
+use ty::{self, Ty, TyCtxt, ToPredicate};
 use ty::outlives::Component;
 use util::nodemap::FxHashSet;
 use hir::{self};
@@ -140,7 +140,7 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
                 let mut predicates: Vec<_> =
                     predicates.predicates
                               .iter()
-                              .map(|p| p.subst_supertrait(tcx, &data.to_poly_trait_ref()))
+                              .map(|p| p.subst_supertrait(tcx, data))
                               .collect();
 
                 debug!("super_predicates: data={:?} predicates={:?}",
@@ -348,7 +348,7 @@ impl<'tcx,I:Iterator<Item=ty::Predicate<'tcx>>> Iterator for FilterToTraits<I> {
                     return None;
                 }
                 Some(ty::Predicate::Trait(data)) => {
-                    return Some(data.to_poly_trait_ref());
+                    return Some(data);
                 }
                 Some(_) => {
                 }
diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs
index 24c19bfc3f3f0..d222e0673e7f7 100644
--- a/src/librustc/ty/item_path.rs
+++ b/src/librustc/ty/item_path.rs
@@ -251,7 +251,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             // the module more clearly.
             self.push_item_path(buffer, parent_def_id);
             if let Some(trait_ref) = impl_trait_ref {
-                buffer.push(&format!("<impl {} for {}>", trait_ref, self_ty));
+                buffer.push(&format!("<impl {} for {}>", trait_ref.print_without_self(), self_ty));
             } else {
                 buffer.push(&format!("<impl {}>", self_ty));
             }
@@ -265,7 +265,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             // Trait impls.
             buffer.push(&format!("<{} as {}>",
                                  self_ty,
-                                 trait_ref));
+                                 trait_ref.print_without_self()));
             return;
         }
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 666a20df15ec6..c013aaa424891 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -64,6 +64,7 @@ pub use self::sty::{FnSig, GenSig, PolyFnSig, PolyGenSig};
 pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
 pub use self::sty::{ClosureSubsts, GeneratorInterior, TypeAndMut};
 pub use self::sty::{TraitRef, TypeVariants, PolyTraitRef};
+pub use self::sty::{TraitRefPrintWithoutSelf, TraitRefPrintWithColon};
 pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef};
 pub use self::sty::{ExistentialProjection, PolyExistentialProjection, Const};
 pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region};
@@ -865,7 +866,7 @@ pub enum Predicate<'tcx> {
     /// Corresponds to `where Foo : Bar<A,B,C>`. `Foo` here would be
     /// the `Self` type of the trait reference and `A`, `B`, and `C`
     /// would be the type parameters.
-    Trait(PolyTraitPredicate<'tcx>),
+    Trait(PolyTraitRef<'tcx>),
 
     /// where `T1 == T2`.
     Equate(PolyEquatePredicate<'tcx>),
@@ -994,33 +995,6 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
-pub struct TraitPredicate<'tcx> {
-    pub trait_ref: TraitRef<'tcx>
-}
-pub type PolyTraitPredicate<'tcx> = ty::Binder<TraitPredicate<'tcx>>;
-
-impl<'tcx> TraitPredicate<'tcx> {
-    pub fn def_id(&self) -> DefId {
-        self.trait_ref.def_id
-    }
-
-    pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator<Item=Ty<'tcx>> + 'a {
-        self.trait_ref.input_types()
-    }
-
-    pub fn self_ty(&self) -> Ty<'tcx> {
-        self.trait_ref.self_ty()
-    }
-}
-
-impl<'tcx> PolyTraitPredicate<'tcx> {
-    pub fn def_id(&self) -> DefId {
-        // ok to skip binder since trait def-id does not care about regions
-        self.0.def_id()
-    }
-}
-
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct EquatePredicate<'tcx>(pub Ty<'tcx>, pub Ty<'tcx>); // `0 == 1`
 pub type PolyEquatePredicate<'tcx> = ty::Binder<EquatePredicate<'tcx>>;
@@ -1086,32 +1060,19 @@ impl<'tcx> ToPolyTraitRef<'tcx> for TraitRef<'tcx> {
     }
 }
 
-impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> {
-    fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> {
-        self.map_bound_ref(|trait_pred| trait_pred.trait_ref)
-    }
-}
-
 pub trait ToPredicate<'tcx> {
     fn to_predicate(&self) -> Predicate<'tcx>;
 }
 
 impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> {
     fn to_predicate(&self) -> Predicate<'tcx> {
-        // we're about to add a binder, so let's check that we don't
-        // accidentally capture anything, or else that might be some
-        // weird debruijn accounting.
-        assert!(!self.has_escaping_regions());
-
-        ty::Predicate::Trait(ty::Binder(ty::TraitPredicate {
-            trait_ref: self.clone()
-        }))
+        ty::Predicate::Trait(self.to_poly_trait_ref())
     }
 }
 
 impl<'tcx> ToPredicate<'tcx> for PolyTraitRef<'tcx> {
     fn to_predicate(&self) -> Predicate<'tcx> {
-        ty::Predicate::Trait(self.to_poly_trait_predicate())
+        ty::Predicate::Trait(*self)
     }
 }
 
@@ -1187,8 +1148,8 @@ impl<'tcx> Predicate<'tcx> {
 
     pub fn to_opt_poly_trait_ref(&self) -> Option<PolyTraitRef<'tcx>> {
         match *self {
-            Predicate::Trait(ref t) => {
-                Some(t.to_poly_trait_ref())
+            Predicate::Trait(t) => {
+                Some(t)
             }
             Predicate::Projection(..) |
             Predicate::Equate(..) |
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 15257fc5a277e..af3f313cbd2d1 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -99,6 +99,20 @@ impl<'a, 'tcx> Lift<'tcx> for ty::TraitRef<'a> {
     }
 }
 
+impl<'a, 'tcx> Lift<'tcx> for ty::TraitRefPrintWithoutSelf<'a> {
+    type Lifted = ty::TraitRefPrintWithoutSelf<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitRefPrintWithoutSelf { trait_ref })
+    }
+}
+
+impl<'a, 'tcx> Lift<'tcx> for ty::TraitRefPrintWithColon<'a> {
+    type Lifted = ty::TraitRefPrintWithColon<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitRefPrintWithColon { trait_ref })
+    }
+}
+
 impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> {
     type Lifted = ty::ExistentialTraitRef<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@@ -109,16 +123,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> {
     }
 }
 
-impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> {
-    type Lifted = ty::TraitPredicate<'tcx>;
-    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>)
-                             -> Option<ty::TraitPredicate<'tcx>> {
-        tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitPredicate {
-            trait_ref,
-        })
-    }
-}
-
 impl<'a, 'tcx> Lift<'tcx> for ty::EquatePredicate<'a> {
     type Lifted = ty::EquatePredicate<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>)
@@ -768,6 +772,30 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for ty::TraitRefPrintWithoutSelf<'tcx> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        ty::TraitRefPrintWithoutSelf {
+            trait_ref: self.trait_ref.fold_with(folder),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.trait_ref.visit_with(visitor)
+    }
+}
+
+impl<'tcx> TypeFoldable<'tcx> for ty::TraitRefPrintWithColon<'tcx> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        ty::TraitRefPrintWithColon {
+            trait_ref: self.trait_ref.fold_with(folder),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.trait_ref.visit_with(visitor)
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialTraitRef<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         ty::ExistentialTraitRef {
@@ -1061,18 +1089,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::SubtypePredicate<'tcx> {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::TraitPredicate<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::TraitPredicate {
-            trait_ref: self.trait_ref.fold_with(folder)
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.trait_ref.visit_with(visitor)
-    }
-}
-
 impl<'tcx,T,U> TypeFoldable<'tcx> for ty::OutlivesPredicate<T,U>
     where T : TypeFoldable<'tcx>,
           U : TypeFoldable<'tcx>,
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index d8ec37dfa682e..0b4d73b6ae56b 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -427,6 +427,12 @@ impl<'tcx> Binder<&'tcx Slice<ExistentialPredicate<'tcx>>> {
 /// Note that a `TraitRef` introduces a level of region binding, to
 /// account for higher-ranked trait bounds like `T : for<'a> Foo<&'a
 /// U>` or higher-ranked object types.
+///
+/// A note on `Display`: there are multiple ways to display a
+/// `TraitRef`; the default does not print the self type. Probably the
+/// default should be removed, but for now, you can explicitly choose
+/// a display style by invoking the methods like
+/// `print_with_colon()` and `print_without_self()`.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub struct TraitRef<'tcx> {
     pub def_id: DefId,
@@ -449,32 +455,62 @@ impl<'tcx> TraitRef<'tcx> {
         // associated types.
         self.substs.types()
     }
+
+    /// Returns a wrapper that implements `Display` and displays like
+    /// `P0: Trait<..>`.
+    pub fn print_with_colon(self) -> TraitRefPrintWithColon<'tcx> {
+        TraitRefPrintWithColon { trait_ref: self }
+    }
+
+    /// Returns a wrapper that implements `Display` and displays like
+    /// `Trait<..>`.
+    pub fn print_without_self(self) -> TraitRefPrintWithoutSelf<'tcx> {
+        TraitRefPrintWithoutSelf { trait_ref: self }
+    }
+}
+
+/// Wrapper around TraitRef used only for its `Display` impl.
+/// This version displays like `P0: Trait<P1...Pn>`.
+#[derive(Copy, Clone)]
+pub struct TraitRefPrintWithColon<'tcx> {
+    pub trait_ref: TraitRef<'tcx>
+}
+
+/// Wrapper around TraitRef used only for its `Display` impl.
+/// This version displays like `Trait<P1...Pn>`.
+#[derive(Copy, Clone)]
+pub struct TraitRefPrintWithoutSelf<'tcx> {
+    pub trait_ref: TraitRef<'tcx>
 }
 
 pub type PolyTraitRef<'tcx> = Binder<TraitRef<'tcx>>;
 
 impl<'tcx> PolyTraitRef<'tcx> {
     pub fn self_ty(&self) -> Ty<'tcx> {
-        self.0.self_ty()
+        // FIXME(#20664) every use of this fn is probably a bug, it should yield Binder<>
+        self.skip_binder().self_ty()
     }
 
     pub fn def_id(&self) -> DefId {
-        self.0.def_id
+        self.skip_binder().def_id
     }
 
     pub fn substs(&self) -> &'tcx Substs<'tcx> {
         // FIXME(#20664) every use of this fn is probably a bug, it should yield Binder<>
-        self.0.substs
+        self.skip_binder().substs
     }
 
     pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator<Item=Ty<'tcx>> + 'a {
         // FIXME(#20664) every use of this fn is probably a bug, it should yield Binder<>
-        self.0.input_types()
+        self.skip_binder().input_types()
+    }
+
+    pub fn print_with_colon(self) -> Binder<TraitRefPrintWithColon<'tcx>> {
+        self.map_bound(|t| t.print_with_colon())
     }
 
-    pub fn to_poly_trait_predicate(&self) -> ty::PolyTraitPredicate<'tcx> {
-        // Note that we preserve binding levels
-        Binder(ty::TraitPredicate { trait_ref: self.0.clone() })
+    pub fn print_without_self(self) -> Binder<TraitRefPrintWithoutSelf<'tcx>> {
+        self.map_bound(|t| t.print_without_self())
     }
 }
 
@@ -521,12 +557,12 @@ pub type PolyExistentialTraitRef<'tcx> = Binder<ExistentialTraitRef<'tcx>>;
 
 impl<'tcx> PolyExistentialTraitRef<'tcx> {
     pub fn def_id(&self) -> DefId {
-        self.0.def_id
+        self.skip_binder().def_id
     }
 
     pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator<Item=Ty<'tcx>> + 'a {
         // FIXME(#20664) every use of this fn is probably a bug, it should yield Binder<>
-        self.0.input_types()
+        self.skip_binder().input_types()
     }
 }
 
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs
index c631e2c4db51b..eeed684b5698c 100644
--- a/src/librustc/ty/wf.rs
+++ b/src/librustc/ty/wf.rs
@@ -75,7 +75,7 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     // (*) ok to skip binders, because wf code is prepared for it
     match *predicate {
         ty::Predicate::Trait(ref t) => {
-            wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*)
+            wf.compute_trait_ref(&t.skip_binder(), Elaborate::None); // (*)
         }
         ty::Predicate::Equate(ref t) => {
             wf.compute(t.skip_binder().0);
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index ba147443a71dc..c2cdc19fa2ee2 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -908,9 +908,10 @@ impl fmt::Debug for ty::FloatVarValue {
 define_print_multi! {
     [
     ('tcx) ty::Binder<&'tcx ty::Slice<ty::ExistentialPredicate<'tcx>>>,
-    ('tcx) ty::Binder<ty::TraitRef<'tcx>>,
+        // ('tcx) ty::Binder<ty::TraitRef<'tcx>> is intentionally omited
+    ('tcx) ty::Binder<ty::TraitRefPrintWithColon<'tcx>>,
+    ('tcx) ty::Binder<ty::TraitRefPrintWithoutSelf<'tcx>>,
     ('tcx) ty::Binder<ty::FnSig<'tcx>>,
-    ('tcx) ty::Binder<ty::TraitPredicate<'tcx>>,
     ('tcx) ty::Binder<ty::EquatePredicate<'tcx>>,
     ('tcx) ty::Binder<ty::SubtypePredicate<'tcx>>,
     ('tcx) ty::Binder<ty::ProjectionPredicate<'tcx>>,
@@ -926,19 +927,44 @@ define_print_multi! {
 
 define_print! {
     ('tcx) ty::TraitRef<'tcx>, (self, f, cx) {
-        display {
-            cx.parameterized(f, self.substs, self.def_id, &[])
-        }
+        // NB -- no `display` impl. Instead, use `trait_ref.print_without_self()`
+        // or `trait_ref.print_with_colon()`.
+        //
+        // display { .. }
+
         debug {
             // when printing out the debug representation, we don't need
             // to enumerate the `for<...>` etc because the debruijn index
             // tells you everything you need to know.
+            print!(f, cx, write("<"), print(self.print_with_colon()), write(">"))
+        }
+    }
+}
+
+define_print! {
+    ('tcx) ty::TraitRefPrintWithoutSelf<'tcx>, (self, f, cx) {
+        display {
+            cx.parameterized(f, self.trait_ref.substs, self.trait_ref.def_id, &[])
+        }
+        debug {
+            cx.parameterized(f, self.trait_ref.substs, self.trait_ref.def_id, &[])
+        }
+    }
+}
+
+define_print! {
+    ('tcx) ty::TraitRefPrintWithColon<'tcx>, (self, f, cx) {
+        display {
             print!(f, cx,
-                   write("<"),
-                   print(self.self_ty()),
-                   write(" as "))?;
-            cx.parameterized(f, self.substs, self.def_id, &[])?;
-            write!(f, ">")
+                   print(self.trait_ref.self_ty()),
+                   write(": "),
+                   print(self.trait_ref.print_without_self()))
+        }
+        debug {
+            print!(f, cx,
+                   print(self.trait_ref.self_ty()),
+                   write(": "),
+                   print(self.trait_ref.print_without_self()))
         }
     }
 }
@@ -1043,7 +1069,7 @@ define_print! {
 
                                 print!(f, cx,
                                        write("{}", if first { " " } else { "+" }),
-                                       print(trait_ref))?;
+                                       print(trait_ref.print_without_self()))?;
                                 first = false;
                             }
                         }
@@ -1194,18 +1220,6 @@ define_print! {
     }
 }
 
-define_print! {
-    ('tcx) ty::TraitPredicate<'tcx>, (self, f, cx) {
-        debug {
-            write!(f, "TraitPredicate({:?})",
-                   self.trait_ref)
-        }
-        display {
-            print!(f, cx, print(self.trait_ref.self_ty()), write(": "), print(self.trait_ref))
-        }
-    }
-}
-
 define_print! {
     ('tcx) ty::ProjectionPredicate<'tcx>, (self, f, cx) {
         debug {
@@ -1252,7 +1266,7 @@ define_print! {
     ('tcx) ty::Predicate<'tcx>, (self, f, cx) {
         display {
             match *self {
-                ty::Predicate::Trait(ref data) => data.print(f, cx),
+                ty::Predicate::Trait(ref data) => data.print_with_colon().print(f, cx),
                 ty::Predicate::Equate(ref predicate) => predicate.print(f, cx),
                 ty::Predicate::Subtype(ref predicate) => predicate.print(f, cx),
                 ty::Predicate::RegionOutlives(ref predicate) => predicate.print(f, cx),
@@ -1277,7 +1291,7 @@ define_print! {
         }
         debug {
             match *self {
-                ty::Predicate::Trait(ref a) => a.print(f, cx),
+                ty::Predicate::Trait(ref a) => a.print_with_colon().print(f, cx),
                 ty::Predicate::Equate(ref pair) => pair.print(f, cx),
                 ty::Predicate::Subtype(ref pair) => pair.print(f, cx),
                 ty::Predicate::RegionOutlives(ref pair) => pair.print(f, cx),
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 07874a8cc69dd..e67fc59399034 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1030,7 +1030,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
                     let obligation =
                         traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
                                                 cx.param_env,
-                                                trait_ref.to_poly_trait_predicate());
+                                                trait_ref);
 
                     tcx.infer_ctxt().enter(|infcx| {
                         let mut selcx = traits::SelectionContext::new(&infcx);
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 630260feed789..c628876a16fd8 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -400,7 +400,7 @@ impl<'b, 'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
             predicate.visit_with(self);
             match predicate {
                 &ty::Predicate::Trait(poly_predicate) => {
-                    self.check_trait_ref(poly_predicate.skip_binder().trait_ref);
+                    self.check_trait_ref(*poly_predicate.skip_binder());
                 },
                 &ty::Predicate::Projection(poly_predicate) => {
                     let tcx = self.ev.tcx;
@@ -854,7 +854,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
             ty::TyProjection(ref proj) => {
                 let trait_ref = proj.trait_ref(self.tcx);
                 if !self.item_is_accessible(trait_ref.def_id) {
-                    let msg = format!("trait `{}` is private", trait_ref);
+                    let msg = format!("trait `{}` is private", trait_ref.print_without_self());
                     self.tcx.sess.span_err(self.span, &msg);
                     return true;
                 }
@@ -866,7 +866,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
                 for predicate in &self.tcx.predicates_of(def_id).predicates {
                     let trait_ref = match *predicate {
                         ty::Predicate::Trait(ref poly_trait_predicate) => {
-                            Some(poly_trait_predicate.skip_binder().trait_ref)
+                            Some(*poly_trait_predicate.skip_binder())
                         }
                         ty::Predicate::Projection(ref poly_projection_predicate) => {
                             if poly_projection_predicate.skip_binder().ty.visit_with(self) {
@@ -880,7 +880,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
                     };
                     if let Some(trait_ref) = trait_ref {
                         if !self.item_is_accessible(trait_ref.def_id) {
-                            let msg = format!("trait `{}` is private", trait_ref);
+                            let msg = format!("trait `{}` is private", trait_ref.print_without_self());
                             self.tcx.sess.span_err(self.span, &msg);
                             return true;
                         }
@@ -1279,7 +1279,7 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
             predicate.visit_with(self);
             match predicate {
                 &ty::Predicate::Trait(poly_predicate) => {
-                    self.check_trait_ref(poly_predicate.skip_binder().trait_ref);
+                    self.check_trait_ref(*poly_predicate.skip_binder());
                 },
                 &ty::Predicate::Projection(poly_predicate) => {
                     let tcx = self.tcx;
@@ -1323,7 +1323,7 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
             if !vis.is_at_least(self.required_visibility, self.tcx) {
                 if self.has_pub_restricted || self.has_old_errors {
                     struct_span_err!(self.tcx.sess, self.span, E0445,
-                                     "private trait `{}` in public interface", trait_ref)
+                                     "private trait `{}` in public interface", trait_ref.print_without_self())
                         .span_label(self.span, format!(
                                     "private trait can't be public"))
                         .emit();
@@ -1332,7 +1332,7 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
                                        node_id,
                                        self.span,
                                        &format!("private trait `{}` in public \
-                                                 interface (error E0445)", trait_ref));
+                                                 interface (error E0445)", trait_ref.print_without_self()));
                 }
             }
         }
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index bbde41a3f4d83..82ba53c4e0377 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -501,7 +501,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
             .filter(|r| self.trait_defines_associated_type_named(r.def_id(), binding.item_name));
 
         let candidate = self.one_bound_for_assoc_type(candidates,
-                                                      &trait_ref.to_string(),
+                                                      &trait_ref.print_without_self().to_string(),
                                                       binding.item_name,
                                                       binding.span)?;
 
@@ -747,12 +747,12 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                 if let Some(span) = bound_span {
                     err.span_label(span, format!("ambiguous `{}` from `{}`",
                                                   assoc_name,
-                                                  bound));
+                                                  bound.print_without_self()));
                 } else {
                     span_note!(&mut err, span,
                                "associated type `{}` could derive from `{}`",
                                ty_param_name,
-                               bound);
+                               bound.print_without_self());
                 }
             }
             err.emit();
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index fa5a7c25c0c22..ee0e29c1bb6e3 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -17,7 +17,7 @@ use rustc::hir::def_id::DefId;
 use rustc::infer::{InferOk, InferResult};
 use rustc::infer::LateBoundRegionConversionTime;
 use rustc::infer::type_variable::TypeVariableOrigin;
-use rustc::ty::{self, ToPolyTraitRef, Ty};
+use rustc::ty::{self, Ty};
 use rustc::ty::subst::Substs;
 use rustc::ty::TypeFoldable;
 use std::cmp;
@@ -220,7 +220,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             .filter_map(|obligation| {
                 let opt_trait_ref = match obligation.predicate {
                     ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref(self.tcx)),
-                    ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
+                    ty::Predicate::Trait(data) => Some(data),
                     ty::Predicate::Equate(..) => None,
                     ty::Predicate::Subtype(..) => None,
                     ty::Predicate::RegionOutlives(..) => None,
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 3cb58beaa2233..1638719a21ef7 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -19,7 +19,7 @@ use hir::def::Def;
 use namespace::Namespace;
 use rustc::ty::subst::{Subst, Substs};
 use rustc::traits::{self, ObligationCause};
-use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
+use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
 use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::util::nodemap::FxHashSet;
 use rustc::infer::{self, InferOk};
@@ -590,10 +590,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             .iter()
             .filter_map(|predicate| {
                 match *predicate {
-                    ty::Predicate::Trait(ref trait_predicate) => {
-                        match trait_predicate.0.trait_ref.self_ty().sty {
+                    ty::Predicate::Trait(trait_predicate) => {
+                        match trait_predicate.skip_binder().self_ty().sty {
                             ty::TyParam(ref p) if *p == param_ty => {
-                                Some(trait_predicate.to_poly_trait_ref())
+                                Some(trait_predicate)
                             }
                             _ => None,
                         }
@@ -957,8 +957,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                               -> traits::SelectionResult<'tcx, traits::Selection<'tcx>>
     {
         let cause = traits::ObligationCause::misc(self.span, self.body_id);
-        let predicate =
-            trait_ref.to_poly_trait_ref().to_poly_trait_predicate();
+        let predicate = trait_ref.to_poly_trait_ref();
         let obligation = traits::Obligation::new(cause, self.param_env, predicate);
         traits::SelectionContext::new(self).select(&obligation)
     }
@@ -1074,7 +1073,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                 if !selcx.evaluate_obligation(&o) {
                     result = ProbeResult::NoMatch;
                     if let &ty::Predicate::Trait(ref pred) = &o.predicate {
-                        possibly_unsatisfied_predicates.push(pred.0.trait_ref);
+                        possibly_unsatisfied_predicates.push(*pred.skip_binder());
                     }
                 }
             }
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index bdb56b7da94e6..91100c626cf83 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -272,7 +272,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
                 if !unsatisfied_predicates.is_empty() {
                     let bound_list = unsatisfied_predicates.iter()
-                        .map(|p| format!("`{} : {}`", p.self_ty(), p))
+                        .map(|p| p.print_with_colon().to_string())
                         .collect::<Vec<_>>()
                         .join("\n");
                     err.note(&format!("the method `{}` exists but the following trait bounds \
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index e65d3f7a9bd7a..0c44f9ebba2c2 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1331,7 +1331,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                   "item `{}` is an associated const, \
                                   which doesn't match its trait `{}`",
                                   ty_impl_item.name,
-                                  impl_trait_ref);
+                                  impl_trait_ref.print_without_self());
                          err.span_label(impl_item.span, "does not match trait");
                          // We can only get the spans from local trait definition
                          // Same for E0324 and E0325
@@ -1367,7 +1367,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                   "item `{}` is an associated method, \
                                   which doesn't match its trait `{}`",
                                   ty_impl_item.name,
-                                  impl_trait_ref);
+                                  impl_trait_ref.print_without_self());
                          err.span_label(impl_item.span, "does not match trait");
                          if let Some(trait_span) = tcx.hir.span_if_local(ty_trait_item.def_id) {
                             err.span_label(trait_span, "item in trait");
@@ -1385,7 +1385,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                   "item `{}` is an associated type, \
                                   which doesn't match its trait `{}`",
                                   ty_impl_item.name,
-                                  impl_trait_ref);
+                                  impl_trait_ref.print_without_self());
                          err.span_label(impl_item.span, "does not match trait");
                          if let Some(trait_span) = tcx.hir.span_if_local(ty_trait_item.def_id) {
                             err.span_label(trait_span, "item in trait");
diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs
index 5cc6eaa5602fb..196ab5b486c72 100644
--- a/src/librustc_typeck/coherence/overlap.rs
+++ b/src/librustc_typeck/coherence/overlap.rs
@@ -88,7 +88,7 @@ impl<'cx, 'tcx, 'v> ItemLikeVisitor<'v> for OverlapChecker<'cx, 'tcx> {
                                                    E0521,
                                                    "redundant auto implementations of trait \
                                                     `{}`:",
-                                                   trait_ref);
+                                                   trait_ref.print_without_self());
                     err.span_note(self.tcx
                                       .span_of_impl(self.tcx.hir.local_def_id(prev_id))
                                       .unwrap(),
diff --git a/src/librustc_typeck/coherence/unsafety.rs b/src/librustc_typeck/coherence/unsafety.rs
index 280fb04e04001..71fb2eacd1556 100644
--- a/src/librustc_typeck/coherence/unsafety.rs
+++ b/src/librustc_typeck/coherence/unsafety.rs
@@ -49,7 +49,7 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
                                   item.span,
                                   E0199,
                                   "implementing the trait `{}` is not unsafe",
-                                  trait_ref);
+                                  trait_ref.print_without_self());
                     }
 
                     (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
@@ -57,7 +57,7 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
                                   item.span,
                                   E0200,
                                   "the trait `{}` requires an `unsafe impl` declaration",
-                                  trait_ref);
+                                  trait_ref.print_without_self());
                     }
 
                     (Unsafety::Normal, Some(g), Unsafety::Normal, hir::ImplPolarity::Positive) =>
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 4b60536e1d176..59c5ae92bbc8c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -978,15 +978,6 @@ impl<'a> Clean<WherePredicate> for ty::Predicate<'a> {
     }
 }
 
-impl<'a> Clean<WherePredicate> for ty::TraitPredicate<'a> {
-    fn clean(&self, cx: &DocContext) -> WherePredicate {
-        WherePredicate::BoundPredicate {
-            ty: self.trait_ref.self_ty().clean(cx),
-            bounds: vec![self.trait_ref.clean(cx)]
-        }
-    }
-}
-
 impl<'tcx> Clean<WherePredicate> for ty::EquatePredicate<'tcx> {
     fn clean(&self, cx: &DocContext) -> WherePredicate {
         let ty::EquatePredicate(ref lhs, ref rhs) = *self;

From 1e4a936503f01cbaa05d54e230bdea13916bde75 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 3 Sep 2017 05:10:38 -0400
Subject: [PATCH 38/63] s/TraitObligation/PolyTraitObligation/

---
 src/librustc/traits/mod.rs    |  5 +-
 src/librustc/traits/select.rs | 90 +++++++++++++++++------------------
 2 files changed, 47 insertions(+), 48 deletions(-)

diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 183226e985a80..4579e76f953f6 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -86,7 +86,7 @@ pub struct Obligation<'tcx, T> {
 }
 
 pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
-pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitRef<'tcx>>;
+pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitRef<'tcx>>;
 
 /// Why did we incur this obligation? Used for error reporting.
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -219,7 +219,6 @@ pub struct DerivedObligationCause<'tcx> {
 
 pub type Obligations<'tcx, O> = Vec<Obligation<'tcx, O>>;
 pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>;
-pub type TraitObligations<'tcx> = Vec<TraitObligation<'tcx>>;
 
 pub type Selection<'tcx> = Vtable<'tcx, PredicateObligation<'tcx>>;
 
@@ -844,7 +843,7 @@ impl<'tcx> FulfillmentError<'tcx> {
     }
 }
 
-impl<'tcx> TraitObligation<'tcx> {
+impl<'tcx> PolyTraitObligation<'tcx> {
     fn self_ty(&self) -> ty::Binder<Ty<'tcx>> {
         ty::Binder(self.predicate.skip_binder().self_ty())
     }
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 753c5ecfb5941..099d7bb43f225 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -17,7 +17,7 @@ use super::coherence;
 use super::DerivedObligationCause;
 use super::project;
 use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
-use super::{PredicateObligation, TraitObligation, ObligationCause};
+use super::{PredicateObligation, PolyTraitObligation, ObligationCause};
 use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
 use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
 use super::{ObjectCastObligation, Obligation};
@@ -132,7 +132,7 @@ impl IntercrateAmbiguityCause {
 
 // A stack that walks back up the stack frame.
 struct TraitObligationStack<'prev, 'tcx: 'prev> {
-    obligation: &'prev TraitObligation<'tcx>,
+    obligation: &'prev PolyTraitObligation<'tcx>,
 
     /// Trait ref from `obligation` but skolemized with the
     /// selection-context's freshener. Used to check for recursion.
@@ -505,7 +505,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Attempts to satisfy the obligation. If successful, this will affect the surrounding
     /// type environment by performing unification.
-    pub fn select(&mut self, obligation: &TraitObligation<'tcx>)
+    pub fn select(&mut self, obligation: &PolyTraitObligation<'tcx>)
                   -> SelectionResult<'tcx, Selection<'tcx>> {
         debug!("select({:?})", obligation);
         assert!(!obligation.predicate.has_escaping_regions());
@@ -750,7 +750,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn evaluate_trait_predicate_recursively<'o>(&mut self,
                                                 previous_stack: TraitObligationStackList<'o, 'tcx>,
-                                                mut obligation: TraitObligation<'tcx>)
+                                                mut obligation: PolyTraitObligation<'tcx>)
                                                 -> EvaluationResult
     {
         debug!("evaluate_trait_predicate_recursively({:?})",
@@ -1378,7 +1378,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn assemble_candidates_from_projected_tys(&mut self,
-                                              obligation: &TraitObligation<'tcx>,
+                                              obligation: &PolyTraitObligation<'tcx>,
                                               candidates: &mut SelectionCandidateSet<'tcx>)
     {
         debug!("assemble_candidates_for_projected_tys({:?})", obligation);
@@ -1405,7 +1405,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn match_projection_obligation_against_definition_bounds(
         &mut self,
-        obligation: &TraitObligation<'tcx>)
+        obligation: &PolyTraitObligation<'tcx>)
         -> bool
     {
         let poly_trait_predicate =
@@ -1466,7 +1466,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn match_projection(&mut self,
-                        obligation: &TraitObligation<'tcx>,
+                        obligation: &PolyTraitObligation<'tcx>,
                         trait_bound: ty::PolyTraitRef<'tcx>,
                         skol_trait_ref: ty::TraitRef<'tcx>)
                         -> bool
@@ -1532,7 +1532,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn assemble_generator_candidates(&mut self,
-                                   obligation: &TraitObligation<'tcx>,
+                                   obligation: &PolyTraitObligation<'tcx>,
                                    candidates: &mut SelectionCandidateSet<'tcx>)
                                    -> Result<(),SelectionError<'tcx>>
     {
@@ -1569,7 +1569,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// parameters and hence do not affect whether this trait is a match or not. They will be
     /// unified during the confirmation step.
     fn assemble_closure_candidates(&mut self,
-                                   obligation: &TraitObligation<'tcx>,
+                                   obligation: &PolyTraitObligation<'tcx>,
                                    candidates: &mut SelectionCandidateSet<'tcx>)
                                    -> Result<(),SelectionError<'tcx>>
     {
@@ -1610,7 +1610,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Implement one of the `Fn()` family for a fn pointer.
     fn assemble_fn_pointer_candidates(&mut self,
-                                      obligation: &TraitObligation<'tcx>,
+                                      obligation: &PolyTraitObligation<'tcx>,
                                       candidates: &mut SelectionCandidateSet<'tcx>)
                                       -> Result<(),SelectionError<'tcx>>
     {
@@ -1647,7 +1647,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Search for impls that might apply to `obligation`.
     fn assemble_candidates_from_impls(&mut self,
-                                      obligation: &TraitObligation<'tcx>,
+                                      obligation: &PolyTraitObligation<'tcx>,
                                       candidates: &mut SelectionCandidateSet<'tcx>)
                                       -> Result<(), SelectionError<'tcx>>
     {
@@ -1672,9 +1672,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn assemble_candidates_from_auto_impls(&mut self,
-                                              obligation: &TraitObligation<'tcx>,
-                                              candidates: &mut SelectionCandidateSet<'tcx>)
-                                              -> Result<(), SelectionError<'tcx>>
+                                           obligation: &PolyTraitObligation<'tcx>,
+                                           candidates: &mut SelectionCandidateSet<'tcx>)
+                                           -> Result<(), SelectionError<'tcx>>
     {
         // OK to skip binder here because the tests we do below do not involve bound regions
         let self_ty = *obligation.self_ty().skip_binder();
@@ -1727,7 +1727,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Search for impls that might apply to `obligation`.
     fn assemble_candidates_from_object_ty(&mut self,
-                                          obligation: &TraitObligation<'tcx>,
+                                          obligation: &PolyTraitObligation<'tcx>,
                                           candidates: &mut SelectionCandidateSet<'tcx>)
     {
         debug!("assemble_candidates_from_object_ty(self_ty={:?})",
@@ -1803,7 +1803,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Search for unsizing that might apply to `obligation`.
     fn assemble_candidates_for_unsizing(&mut self,
-                                        obligation: &TraitObligation<'tcx>,
+                                        obligation: &PolyTraitObligation<'tcx>,
                                         candidates: &mut SelectionCandidateSet<'tcx>) {
         // We currently never consider higher-ranked obligations e.g.
         // `for<'a> &'a T: Unsize<Trait+'a>` to be implemented. This is not
@@ -1993,7 +1993,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn sized_conditions(&mut self, obligation: &TraitObligation<'tcx>)
+    fn sized_conditions(&mut self, obligation: &PolyTraitObligation<'tcx>)
                      -> BuiltinImplConditions<'tcx>
     {
         use self::BuiltinImplConditions::{Ambiguous, None, Never, Where};
@@ -2039,8 +2039,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn copy_clone_conditions(&mut self, obligation: &TraitObligation<'tcx>)
-                     -> BuiltinImplConditions<'tcx>
+    fn copy_clone_conditions(&mut self, obligation: &PolyTraitObligation<'tcx>)
+                             -> BuiltinImplConditions<'tcx>
     {
         // NOTE: binder moved to (*)
         let self_ty = self.infcx.shallow_resolve(
@@ -2248,7 +2248,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     // type error.  See `README.md` for more details.
 
     fn confirm_candidate(&mut self,
-                         obligation: &TraitObligation<'tcx>,
+                         obligation: &PolyTraitObligation<'tcx>,
                          candidate: SelectionCandidate<'tcx>)
                          -> Result<Selection<'tcx>,SelectionError<'tcx>>
     {
@@ -2319,7 +2319,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_projection_candidate(&mut self,
-                                    obligation: &TraitObligation<'tcx>)
+                                    obligation: &PolyTraitObligation<'tcx>)
     {
         self.in_snapshot(|this, _snapshot| {
             let result =
@@ -2329,7 +2329,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_param_candidate(&mut self,
-                               obligation: &TraitObligation<'tcx>,
+                               obligation: &PolyTraitObligation<'tcx>,
                                param: ty::PolyTraitRef<'tcx>)
                                -> Vec<PredicateObligation<'tcx>>
     {
@@ -2352,7 +2352,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_builtin_candidate(&mut self,
-                                 obligation: &TraitObligation<'tcx>,
+                                 obligation: &PolyTraitObligation<'tcx>,
                                  has_nested: bool)
                                  -> VtableBuiltinData<PredicateObligation<'tcx>>
     {
@@ -2402,9 +2402,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds
     /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds.
     fn confirm_auto_impl_candidate(&mut self,
-                                      obligation: &TraitObligation<'tcx>,
-                                      trait_def_id: DefId)
-                                      -> VtableAutoImplData<PredicateObligation<'tcx>>
+                                   obligation: &PolyTraitObligation<'tcx>,
+                                   trait_def_id: DefId)
+                                   -> VtableAutoImplData<PredicateObligation<'tcx>>
     {
         debug!("confirm_auto_impl_candidate({:?}, {:?})",
                obligation,
@@ -2418,10 +2418,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// See `confirm_auto_impl_candidate`
     fn vtable_auto_impl(&mut self,
-                           obligation: &TraitObligation<'tcx>,
-                           trait_def_id: DefId,
-                           nested: ty::Binder<Vec<Ty<'tcx>>>)
-                           -> VtableAutoImplData<PredicateObligation<'tcx>>
+                        obligation: &PolyTraitObligation<'tcx>,
+                        trait_def_id: DefId,
+                        nested: ty::Binder<Vec<Ty<'tcx>>>)
+                        -> VtableAutoImplData<PredicateObligation<'tcx>>
     {
         debug!("vtable_auto_impl: nested={:?}", nested);
 
@@ -2456,7 +2456,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_impl_candidate(&mut self,
-                              obligation: &TraitObligation<'tcx>,
+                              obligation: &PolyTraitObligation<'tcx>,
                               impl_def_id: DefId)
                               -> VtableImplData<'tcx, PredicateObligation<'tcx>>
     {
@@ -2516,7 +2516,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_object_candidate(&mut self,
-                                obligation: &TraitObligation<'tcx>)
+                                obligation: &PolyTraitObligation<'tcx>)
                                 -> VtableObjectData<'tcx, PredicateObligation<'tcx>>
     {
         debug!("confirm_object_candidate({:?})",
@@ -2578,7 +2578,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn confirm_fn_pointer_candidate(&mut self, obligation: &TraitObligation<'tcx>)
+    fn confirm_fn_pointer_candidate(&mut self, obligation: &PolyTraitObligation<'tcx>)
         -> Result<VtableFnPointerData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
     {
         debug!("confirm_fn_pointer_candidate({:?})",
@@ -2609,7 +2609,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_generator_candidate(&mut self,
-                                   obligation: &TraitObligation<'tcx>)
+                                   obligation: &PolyTraitObligation<'tcx>)
                                    -> Result<VtableGeneratorData<'tcx, PredicateObligation<'tcx>>,
                                            SelectionError<'tcx>>
     {
@@ -2656,7 +2656,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_closure_candidate(&mut self,
-                                 obligation: &TraitObligation<'tcx>)
+                                 obligation: &PolyTraitObligation<'tcx>)
                                  -> Result<VtableClosureData<'tcx, PredicateObligation<'tcx>>,
                                            SelectionError<'tcx>>
     {
@@ -2750,7 +2750,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_builtin_unsize_candidate(&mut self,
-                                        obligation: &TraitObligation<'tcx>,)
+                                        obligation: &PolyTraitObligation<'tcx>,)
         -> Result<VtableBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>>
     {
         let tcx = self.tcx();
@@ -2969,7 +2969,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn rematch_impl(&mut self,
                     impl_def_id: DefId,
-                    obligation: &TraitObligation<'tcx>)
+                    obligation: &PolyTraitObligation<'tcx>)
                     -> (Normalized<'tcx, &'tcx Substs<'tcx>>,
                         ty::ParamEnv<'tcx>)
     {
@@ -2985,7 +2985,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn match_impl(&mut self,
                   impl_def_id: DefId,
-                  obligation: &TraitObligation<'tcx>)
+                  obligation: &PolyTraitObligation<'tcx>)
                   -> Result<(Normalized<'tcx, &'tcx Substs<'tcx>>,
                              ty::ParamEnv<'tcx>), ()>
     {
@@ -3037,7 +3037,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn fast_reject_trait_refs(&mut self,
-                              obligation: &TraitObligation,
+                              obligation: &PolyTraitObligation,
                               impl_trait_ref: &ty::TraitRef)
                               -> bool
     {
@@ -3065,7 +3065,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// because where-clauses are stored in the parameter environment
     /// unnormalized.
     fn match_where_clause_trait_ref(&mut self,
-                                    obligation: &TraitObligation<'tcx>,
+                                    obligation: &PolyTraitObligation<'tcx>,
                                     where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
                                     -> Result<Vec<PredicateObligation<'tcx>>,()>
     {
@@ -3076,7 +3076,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// Returns `Ok` if `poly_trait_ref` being true implies that the
     /// obligation is satisfied.
     fn match_poly_trait_ref(&mut self,
-                            obligation: &TraitObligation<'tcx>,
+                            obligation: &PolyTraitObligation<'tcx>,
                             poly_trait_ref: ty::PolyTraitRef<'tcx>)
                             -> Result<(),()>
     {
@@ -3104,7 +3104,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn push_stack<'o,'s:'o>(&mut self,
                             previous_stack: TraitObligationStackList<'s, 'tcx>,
-                            obligation: &'o TraitObligation<'tcx>)
+                            obligation: &'o PolyTraitObligation<'tcx>)
                             -> TraitObligationStack<'o, 'tcx>
     {
         let fresh_trait_ref = obligation.predicate.fold_with(&mut self.freshener);
@@ -3116,7 +3116,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn closure_trait_ref_unnormalized(&mut self,
-                                      obligation: &TraitObligation<'tcx>,
+                                      obligation: &PolyTraitObligation<'tcx>,
                                       closure_def_id: DefId,
                                       substs: ty::ClosureSubsts<'tcx>)
                                       -> ty::PolyTraitRef<'tcx>
@@ -3138,7 +3138,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn generator_trait_ref_unnormalized(&mut self,
-                                      obligation: &TraitObligation<'tcx>,
+                                      obligation: &PolyTraitObligation<'tcx>,
                                       closure_def_id: DefId,
                                       substs: ty::ClosureSubsts<'tcx>)
                                       -> ty::PolyTraitRef<'tcx>
@@ -3203,7 +3203,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 }
 
-impl<'tcx> TraitObligation<'tcx> {
+impl<'tcx> PolyTraitObligation<'tcx> {
     #[allow(unused_comparisons)]
     pub fn derived_cause(&self,
                         variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>)

From a96ed69e96163928834b9c9f36800410f835aa94 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Tue, 5 Sep 2017 20:20:15 -0400
Subject: [PATCH 39/63] hack up select.rs a bit

---
 src/librustc/traits/select.rs | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 099d7bb43f225..9fa569a7c6e5b 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -1471,14 +1471,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         skol_trait_ref: ty::TraitRef<'tcx>)
                         -> bool
     {
-        assert!(!skol_trait_ref.has_escaping_regions());
-        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);
-                true
-            }
-            Err(_) => false,
+        trait_bound.def_id() == skol_trait_ref.def_id && {
+            assert!(!skol_trait_ref.has_escaping_regions());
+            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);
+                                    true
+                                }
+                                Err(_) => false,
+                            }
         }
     }
 

From 8212a8f52b50d0f742b776cd8ed371050b20e5d2 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 3 Sep 2017 05:59:22 -0400
Subject: [PATCH 40/63] rework selection to skolemize once, at the beginning

---
 src/librustc/traits/error_reporting.rs    |  16 +-
 src/librustc/traits/fulfill.rs            |   2 +-
 src/librustc/traits/mod.rs                |  15 +-
 src/librustc/traits/project.rs            |  10 +-
 src/librustc/traits/select.rs             | 511 ++++++++++------------
 src/librustc/traits/trans/mod.rs          |   2 +-
 src/librustc_lint/builtin.rs              |   2 +-
 src/librustc_typeck/check/coercion.rs     |   2 +-
 src/librustc_typeck/check/method/probe.rs |   5 +-
 src/test/compile-fail/issue-27942.rs      |   4 +-
 10 files changed, 250 insertions(+), 319 deletions(-)

diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 7e69772187f16..5c76ff6f895ce 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -521,7 +521,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     &data.parent_trait_ref);
                 match self.get_parent_trait_ref(&data.parent_code) {
                     Some(t) => Some(t),
-                    None => Some(format!("{}", parent_trait_ref.0.self_ty())),
+                    None => Some(format!("{}", parent_trait_ref.self_ty())),
                 }
             }
             _ => None,
@@ -731,7 +731,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                         _ => 1,
                     };
                 let (expected_tys, expected_ty_count) =
-                    match expected_trait_ref.skip_binder().substs.type_at(1).sty {
+                    match expected_trait_ref.substs.type_at(1).sty {
                         ty::TyTuple(ref tys, _) =>
                             (tys.iter().map(|t| &t.sty).collect(), tys.len()),
                         ref sty => (vec![sty], 1),
@@ -930,11 +930,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                            span: Span,
                            found_span: Option<Span>,
                            expected_ref: ty::PolyTraitRef<'tcx>,
-                           found: ty::PolyTraitRef<'tcx>)
+                           found: ty::TraitRef<'tcx>)
         -> DiagnosticBuilder<'tcx>
     {
         fn build_fn_sig_string<'a, 'gcx, 'tcx>(tcx: ty::TyCtxt<'a, 'gcx, 'tcx>,
-                                               trait_ref: &ty::TraitRef<'tcx>) -> String {
+                                               trait_ref: ty::TraitRef<'tcx>) -> String {
             let inputs = trait_ref.substs.type_at(1);
             let sig = if let ty::TyTuple(inputs, _) = inputs.sty {
                 tcx.mk_fn_sig(
@@ -963,14 +963,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         let found_str = format!(
             "expected signature of `{}`",
-            build_fn_sig_string(self.tcx, found.skip_binder())
+            build_fn_sig_string(self.tcx, found)
         );
         err.span_label(span, found_str);
 
         let found_span = found_span.unwrap_or(span);
         let expected_str = format!(
             "found signature of `{}`",
-            build_fn_sig_string(self.tcx, expected_ref.skip_binder())
+            build_fn_sig_string(self.tcx, *expected_ref.skip_binder())
         );
         err.span_label(found_span, expected_str);
 
@@ -1283,7 +1283,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             ObligationCauseCode::BuiltinDerivedObligation(ref data) => {
                 let parent_trait_ref = self.resolve_type_vars_if_possible(&data.parent_trait_ref);
                 err.note(&format!("required because it appears within the type `{}`",
-                                  parent_trait_ref.0.self_ty()));
+                                  parent_trait_ref.self_ty()));
                 let parent_predicate = parent_trait_ref.to_predicate();
                 self.note_obligation_cause_code(err,
                                                 &parent_predicate,
@@ -1294,7 +1294,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 err.note(
                     &format!("required because of the requirements on the impl of `{}` for `{}`",
                              parent_trait_ref.print_without_self(),
-                             parent_trait_ref.0.self_ty()));
+                             parent_trait_ref.self_ty()));
                 let parent_predicate = parent_trait_ref.to_predicate();
                 self.note_obligation_cause_code(err,
                                                 &parent_predicate,
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index 3e9f8f67ca444..7fe67577858ac 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -374,7 +374,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
                 }
             }
 
-            match selcx.select(&trait_obligation) {
+            match selcx.select_poly(&trait_obligation) {
                 Ok(Some(vtable)) => {
                     debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
                            trait_ref, obligation.recursion_depth);
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 4579e76f953f6..70793f72bae7d 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -87,6 +87,7 @@ pub struct Obligation<'tcx, T> {
 
 pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
 pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitRef<'tcx>>;
+pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitRef<'tcx>>;
 
 /// Why did we incur this obligation? Used for error reporting.
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -211,7 +212,7 @@ pub struct DerivedObligationCause<'tcx> {
     /// current obligation. Note that only trait obligations lead to
     /// derived obligations, so we just store the trait reference here
     /// directly.
-    parent_trait_ref: ty::PolyTraitRef<'tcx>,
+    parent_trait_ref: ty::TraitRef<'tcx>,
 
     /// The parent trait had this cause
     parent_code: Rc<ObligationCauseCode<'tcx>>
@@ -226,7 +227,7 @@ pub type Selection<'tcx> = Vtable<'tcx, PredicateObligation<'tcx>>;
 pub enum SelectionError<'tcx> {
     Unimplemented,
     OutputTypeParameterMismatch(ty::PolyTraitRef<'tcx>,
-                                ty::PolyTraitRef<'tcx>,
+                                ty::TraitRef<'tcx>,
                                 ty::error::TypeError<'tcx>),
     TraitNotObjectSafe(DefId),
     ConstEvalFailure(ConstEvalErr<'tcx>),
@@ -843,9 +844,13 @@ impl<'tcx> FulfillmentError<'tcx> {
     }
 }
 
-impl<'tcx> PolyTraitObligation<'tcx> {
-    fn self_ty(&self) -> ty::Binder<Ty<'tcx>> {
-        ty::Binder(self.predicate.skip_binder().self_ty())
+impl<'tcx> TraitObligation<'tcx> {
+    fn def_id(&self) -> DefId {
+        self.predicate.def_id
+    }
+
+    fn self_ty(&self) -> Ty<'tcx> {
+        self.predicate.self_ty()
     }
 }
 
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 6d5d259dbd2b8..263ecc77d5398 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -984,8 +984,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
 {
     // If we are resolving `<T as TraitRef<...>>::Item == Type`,
     // start out by selecting the predicate `T as TraitRef<...>`:
-    let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
-    let trait_obligation = obligation.with(poly_trait_ref);
+    let trait_obligation = obligation.with(obligation_trait_ref);
     selcx.infcx().probe(|_| {
         let vtable = match selcx.select(&trait_obligation) {
             Ok(Some(vtable)) => vtable,
@@ -1064,8 +1063,8 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
                 let new_candidate = if !is_default {
                     Some(ProjectionTyCandidate::Select)
                 } else if obligation.param_env.reveal == Reveal::All {
-                    assert!(!poly_trait_ref.needs_infer());
-                    if !poly_trait_ref.needs_subst() {
+                    assert!(!obligation_trait_ref.needs_infer());
+                    if !obligation_trait_ref.needs_subst() {
                         Some(ProjectionTyCandidate::Select)
                     } else {
                         None
@@ -1146,8 +1145,7 @@ fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
     obligation_trait_ref: ty::TraitRef<'tcx>)
     -> Progress<'tcx>
 {
-    let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
-    let trait_obligation = obligation.with(poly_trait_ref);
+    let trait_obligation = obligation.with(obligation_trait_ref);
     let vtable = match selcx.select(&trait_obligation) {
         Ok(Some(vtable)) => vtable,
         _ => {
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 9fa569a7c6e5b..4b47909a70049 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -17,7 +17,7 @@ use super::coherence;
 use super::DerivedObligationCause;
 use super::project;
 use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
-use super::{PredicateObligation, PolyTraitObligation, ObligationCause};
+use super::{PredicateObligation, PolyTraitObligation, TraitObligation, ObligationCause};
 use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
 use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
 use super::{ObjectCastObligation, Obligation};
@@ -35,7 +35,7 @@ use hir::def_id::DefId;
 use infer;
 use infer::{InferCtxt, InferOk, TypeFreshener};
 use ty::subst::{Kind, Subst, Substs};
-use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use ty::fast_reject;
 use ty::relate::TypeRelation;
 use middle::lang_items;
@@ -132,11 +132,11 @@ impl IntercrateAmbiguityCause {
 
 // A stack that walks back up the stack frame.
 struct TraitObligationStack<'prev, 'tcx: 'prev> {
-    obligation: &'prev PolyTraitObligation<'tcx>,
+    obligation: &'prev TraitObligation<'tcx>,
 
     /// Trait ref from `obligation` but skolemized with the
     /// selection-context's freshener. Used to check for recursion.
-    fresh_trait_ref: ty::PolyTraitRef<'tcx>,
+    fresh_trait_ref: ty::TraitRef<'tcx>,
 
     previous: TraitObligationStackList<'prev, 'tcx>,
 }
@@ -296,7 +296,7 @@ struct EvaluatedCandidate<'tcx> {
 /// When does the builtin impl for `T: Trait` apply?
 enum BuiltinImplConditions<'tcx> {
     /// The impl is conditional on T1,T2,.. : Trait
-    Where(ty::Binder<Vec<Ty<'tcx>>>),
+    Where(Vec<Ty<'tcx>>),
     /// There is no built-in impl. There may be some other
     /// candidate (a where-clause or user-defined impl).
     None,
@@ -408,7 +408,7 @@ impl EvaluationResult {
 
 #[derive(Clone)]
 pub struct EvaluationCache<'tcx> {
-    hashmap: RefCell<FxHashMap<ty::PolyTraitRef<'tcx>, WithDepNode<EvaluationResult>>>
+    hashmap: RefCell<FxHashMap<ty::TraitRef<'tcx>, WithDepNode<EvaluationResult>>>
 }
 
 impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
@@ -505,10 +505,22 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Attempts to satisfy the obligation. If successful, this will affect the surrounding
     /// type environment by performing unification.
-    pub fn select(&mut self, obligation: &PolyTraitObligation<'tcx>)
+    pub fn select_poly(&mut self, obligation: &PolyTraitObligation<'tcx>)
+                       -> SelectionResult<'tcx, Selection<'tcx>> {
+        debug!("select_poly({:?})", obligation);
+        assert!(!obligation.predicate.has_escaping_regions());
+        let (trait_ref, param_env, _skol_map) =
+            self.infcx().skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
+
+        let obligation = obligation.with(trait_ref).with_env(param_env);
+        self.select(&obligation)
+    }
+
+    /// Attempts to satisfy the obligation. If successful, this will affect the surrounding
+    /// type environment by performing unification.
+    pub fn select(&mut self, obligation: &TraitObligation<'tcx>)
                   -> SelectionResult<'tcx, Selection<'tcx>> {
         debug!("select({:?})", obligation);
-        assert!(!obligation.predicate.has_escaping_regions());
 
         let tcx = self.tcx();
 
@@ -526,7 +538,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // Test whether this is a `()` which was produced by defaulting a
         // diverging type variable with `!` disabled. If so, we may need
         // to raise a warning.
-        if obligation.predicate.skip_binder().self_ty().is_defaulted_unit() {
+        if obligation.self_ty().is_defaulted_unit() {
             let mut raise_warning = true;
             // Don't raise a warning if the trait is implemented for ! and only
             // permits a trivial implementation for !. This stops us warning
@@ -534,8 +546,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // a switch can't cause code to stop compiling or execute
             // differently.
             let mut never_obligation = obligation.clone();
-            let def_id = never_obligation.predicate.def_id();
-            never_obligation.predicate = never_obligation.predicate.map_bound(|mut trait_ref| {
+            let def_id = never_obligation.def_id();
+            never_obligation.predicate = {
+                let mut trait_ref = never_obligation.predicate;
                 // Swap out () with ! so we can check if the trait is impld for !
                 {
                     let unit_substs = trait_ref.substs;
@@ -545,7 +558,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     trait_ref.substs = tcx.intern_substs(&never_substs);
                 }
                 trait_ref
-            });
+            };
             if let Ok(Some(..)) = self.select(&never_obligation) {
                 if !tcx.trait_relevant_for_never(def_id) {
                     // The trait is also implemented for ! and the resulting
@@ -750,20 +763,26 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn evaluate_trait_predicate_recursively<'o>(&mut self,
                                                 previous_stack: TraitObligationStackList<'o, 'tcx>,
-                                                mut obligation: PolyTraitObligation<'tcx>)
+                                                mut poly_obligation: PolyTraitObligation<'tcx>)
                                                 -> EvaluationResult
     {
         debug!("evaluate_trait_predicate_recursively({:?})",
-               obligation);
+               poly_obligation);
 
-        if !self.intercrate && obligation.is_global() {
+        if !self.intercrate && poly_obligation.is_global() {
             // If a param env is consistent, global obligations do not depend on its particular
             // value in order to work, so we can clear out the param env and get better
             // caching. (If the current param env is inconsistent, we don't care what happens).
-            debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation);
-            obligation.param_env = ty::ParamEnv::empty(obligation.param_env.reveal);
+            debug!("evaluate_trait_predicate_recursively({:?}) - in global", poly_obligation);
+            poly_obligation.param_env = ty::ParamEnv::empty(poly_obligation.param_env.reveal);
         }
 
+        let (trait_ref, param_env, _skol_map) =
+            self.infcx().skolemize_late_bound_regions(poly_obligation.param_env,
+                                                      &poly_obligation.predicate);
+
+        let obligation = poly_obligation.with(trait_ref).with_env(param_env);
+
         let stack = self.push_stack(previous_stack, &obligation);
         let fresh_trait_ref = stack.fresh_trait_ref;
         if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) {
@@ -818,7 +837,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // Heuristics: show the diagnostics when there are no candidates in crate.
             if let Ok(candidate_set) = self.assemble_candidates(stack) {
                 if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
-                    let trait_ref = stack.obligation.predicate.skip_binder();
+                    let trait_ref = stack.obligation.predicate;
                     let self_ty = trait_ref.self_ty();
                     let cause = IntercrateAmbiguityCause::DownstreamCrate {
                         trait_desc: trait_ref.print_with_colon().to_string(),
@@ -872,7 +891,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             debug!("evaluate_stack({:?}) --> recursive",
                    stack.fresh_trait_ref);
             let cycle = stack.iter().skip(1).take(rec_index+1);
-            let cycle = cycle.map(|stack| ty::Predicate::Trait(stack.obligation.predicate));
+            let cycle = cycle.map(|stack| {
+                ty::Predicate::Trait(stack.obligation.predicate.to_poly_trait_ref())
+            });
             if self.coinductive_match(cycle) {
                 debug!("evaluate_stack({:?}) --> recursive, coinductive",
                        stack.fresh_trait_ref);
@@ -946,7 +967,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn check_evaluation_cache(&self,
                               param_env: ty::ParamEnv<'tcx>,
-                              trait_ref: ty::PolyTraitRef<'tcx>)
+                              trait_ref: ty::TraitRef<'tcx>)
                               -> Option<EvaluationResult>
     {
         let tcx = self.tcx();
@@ -964,7 +985,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>,
+                               trait_ref: ty::TraitRef<'tcx>,
                                dep_node: DepNodeIndex,
                                result: EvaluationResult)
     {
@@ -1020,7 +1041,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         assert!(!stack.obligation.predicate.has_escaping_regions());
 
         if let Some(c) = self.check_candidate_cache(stack.obligation.param_env,
-                                                    &cache_fresh_trait_pred) {
+                                                    cache_fresh_trait_pred) {
             debug!("CACHE HIT: SELECT({:?})={:?}",
                    cache_fresh_trait_pred,
                    c);
@@ -1082,7 +1103,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // Heuristics: show the diagnostics when there are no candidates in crate.
             let candidate_set = self.assemble_candidates(stack)?;
             if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
-                let trait_ref = *stack.obligation.predicate.skip_binder();
+                let trait_ref = stack.obligation.predicate;
                 let self_ty = trait_ref.self_ty();
                 let trait_desc = trait_ref.print_without_self().to_string();
                 let self_desc = if self_ty.has_concrete_skeleton() {
@@ -1214,13 +1235,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
 
         let obligation = &stack.obligation;
-        let predicate = self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
-
-        // ok to skip binder because of the nature of the
-        // trait-ref-is-knowable check, which does not care about
-        // bound regions
-        let trait_ref = *predicate.skip_binder();
-
+        let trait_ref = self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
         coherence::trait_ref_is_knowable(self.tcx(), trait_ref)
     }
 
@@ -1256,36 +1271,34 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn check_candidate_cache(&mut self,
                              param_env: ty::ParamEnv<'tcx>,
-                             cache_fresh_trait_pred: &ty::PolyTraitRef<'tcx>)
+                             cache_fresh_trait_pred: ty::TraitRef<'tcx>)
                              -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>>
     {
         let tcx = self.tcx();
-        let trait_ref = cache_fresh_trait_pred.skip_binder();
         if self.can_use_global_caches(param_env) {
             let cache = tcx.selection_cache.hashmap.borrow();
-            if let Some(cached) = cache.get(&trait_ref) {
+            if let Some(cached) = cache.get(&cache_fresh_trait_pred) {
                 return Some(cached.get(tcx));
             }
         }
         self.infcx.selection_cache.hashmap
                                   .borrow()
-                                  .get(trait_ref)
+                                  .get(&cache_fresh_trait_pred)
                                   .map(|v| v.get(tcx))
     }
 
     fn insert_candidate_cache(&mut self,
                               param_env: ty::ParamEnv<'tcx>,
-                              cache_fresh_trait_pred: ty::PolyTraitRef<'tcx>,
+                              cache_fresh_trait_pred: ty::TraitRef<'tcx>,
                               dep_node: DepNodeIndex,
                               candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>)
     {
         let tcx = self.tcx();
-        let trait_ref = *cache_fresh_trait_pred.skip_binder();
         if self.can_use_global_caches(param_env) {
             let mut cache = tcx.selection_cache.hashmap.borrow_mut();
-            if let Some(trait_ref) = tcx.lift_to_global(&trait_ref) {
+            if let Some(cache_fresh_trait_pred) = tcx.lift_to_global(&cache_fresh_trait_pred) {
                 if let Some(candidate) = tcx.lift_to_global(&candidate) {
-                    cache.insert(trait_ref, WithDepNode::new(dep_node, candidate));
+                    cache.insert(cache_fresh_trait_pred, WithDepNode::new(dep_node, candidate));
                     return;
                 }
             }
@@ -1293,7 +1306,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         self.infcx.selection_cache.hashmap
                                   .borrow_mut()
-                                  .insert(trait_ref, WithDepNode::new(dep_node, candidate));
+                                  .insert(cache_fresh_trait_pred,
+                                          WithDepNode::new(dep_node, candidate));
     }
 
     fn assemble_candidates<'o>(&mut self,
@@ -1308,7 +1322,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             predicate: self.infcx().resolve_type_vars_if_possible(&obligation.predicate)
         };
 
-        if obligation.predicate.skip_binder().self_ty().is_ty_var() {
+        if obligation.self_ty().is_ty_var() {
             // Self is a type variable (e.g. `_: AsRef<str>`).
             //
             // This is somewhat problematic, as the current scheme can't really
@@ -1329,11 +1343,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // Other bounds. Consider both in-scope bounds from fn decl
         // and applicable impls. There is a certain set of precedence rules here.
 
-        let def_id = obligation.predicate.def_id();
+        let def_id = obligation.def_id();
         let lang_items = self.tcx().lang_items();
         if lang_items.copy_trait() == Some(def_id) {
-            debug!("obligation self ty is {:?}",
-                   obligation.predicate.0.self_ty());
+            debug!("obligation self ty is {:?}", obligation.self_ty());
 
             // User-defined copy impls are permitted, but only for
             // structs and enums.
@@ -1378,14 +1391,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn assemble_candidates_from_projected_tys(&mut self,
-                                              obligation: &PolyTraitObligation<'tcx>,
+                                              obligation: &TraitObligation<'tcx>,
                                               candidates: &mut SelectionCandidateSet<'tcx>)
     {
         debug!("assemble_candidates_for_projected_tys({:?})", obligation);
 
         // before we go into the whole skolemization thing, just
         // quickly check if the self-type is a projection at all.
-        match obligation.predicate.skip_binder().self_ty().sty {
+        match obligation.self_ty().sty {
             ty::TyProjection(_) | ty::TyAnon(..) => {}
             ty::TyInfer(ty::TyVar(_)) => {
                 span_bug!(obligation.cause.span,
@@ -1405,39 +1418,30 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn match_projection_obligation_against_definition_bounds(
         &mut self,
-        obligation: &PolyTraitObligation<'tcx>)
+        obligation: &TraitObligation<'tcx>)
         -> bool
     {
-        let poly_trait_predicate =
-            self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
-        let (skol_trait_predicate, param_env, _skol_map) =
-            self.infcx().skolemize_late_bound_regions(obligation.param_env, &poly_trait_predicate);
-        debug!("match_projection_obligation_against_definition_bounds: \
-                skol_trait_predicate={:?}",
-               skol_trait_predicate);
+        let trait_ref = self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
+        debug!("match_projection_obligation_against_definition_bounds: trait_ref={:?}",
+               trait_ref);
 
-        let obligation = obligation.clone().with_env(param_env);
-
-        let (def_id, substs) = match skol_trait_predicate.self_ty().sty {
-            ty::TyProjection(ref data) =>
-                (data.trait_ref(self.tcx()).def_id, data.substs),
+        let (def_id, substs) = match trait_ref.self_ty().sty {
+            ty::TyProjection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs),
             ty::TyAnon(def_id, substs) => (def_id, substs),
             _ => {
                 span_bug!(
                     obligation.cause.span,
                     "match_projection_obligation_against_definition_bounds() called \
                      but self-ty not a projection: {:?}",
-                    skol_trait_predicate.self_ty());
+                    trait_ref.self_ty());
             }
         };
-        debug!("match_projection_obligation_against_definition_bounds: \
-                def_id={:?}, substs={:?}",
+        debug!("match_projection_obligation_against_definition_bounds: def_id={:?}, substs={:?}",
                def_id, substs);
 
         let predicates_of = self.tcx().predicates_of(def_id);
         let bounds = predicates_of.instantiate(self.tcx(), substs);
-        debug!("match_projection_obligation_against_definition_bounds: \
-                bounds={:?}",
+        debug!("match_projection_obligation_against_definition_bounds: bounds={:?}",
                bounds);
 
         let matching_bound =
@@ -1445,9 +1449,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             .filter_to_traits()
             .find(
                 |bound| self.probe(
-                    |this, _| this.match_projection(&obligation,
+                    |this, _| this.match_projection(obligation,
                                                     bound.clone(),
-                                                    skol_trait_predicate)));
+                                                    trait_ref)));
 
         debug!("match_projection_obligation_against_definition_bounds: \
                 matching_bound={:?}",
@@ -1456,9 +1460,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             None => false,
             Some(bound) => {
                 // Repeat the successful match, if any, this time outside of a probe.
-                let result = self.match_projection(&obligation,
+                let result = self.match_projection(obligation,
                                                    bound,
-                                                   skol_trait_predicate);
+                                                   trait_ref);
                 assert!(result);
                 true
             }
@@ -1466,7 +1470,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn match_projection(&mut self,
-                        obligation: &PolyTraitObligation<'tcx>,
+                        obligation: &TraitObligation<'tcx>,
                         trait_bound: ty::PolyTraitRef<'tcx>,
                         skol_trait_ref: ty::TraitRef<'tcx>)
                         -> bool
@@ -1474,7 +1478,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         trait_bound.def_id() == skol_trait_ref.def_id && {
             assert!(!skol_trait_ref.has_escaping_regions());
             match self.infcx.at(&obligation.cause, obligation.param_env)
-                            .sup(ty::Binder(skol_trait_ref), trait_bound) {
+                            .instantiable_as(trait_bound, skol_trait_ref) {
                                 Ok(InferOk { obligations, .. }) => {
                                     self.inferred_obligations.extend(obligations);
                                     true
@@ -1504,7 +1508,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // micro-optimization: filter out predicates relating to different
         // traits.
         let matching_bounds =
-            all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id());
+            all_bounds.filter(|p| p.def_id() == stack.obligation.def_id());
 
         let matching_bounds =
             matching_bounds.filter(
@@ -1534,18 +1538,18 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn assemble_generator_candidates(&mut self,
-                                   obligation: &PolyTraitObligation<'tcx>,
+                                   obligation: &TraitObligation<'tcx>,
                                    candidates: &mut SelectionCandidateSet<'tcx>)
                                    -> Result<(),SelectionError<'tcx>>
     {
-        if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) {
+        if self.tcx().lang_items().gen_trait() != Some(obligation.def_id()) {
             return Ok(());
         }
 
         // ok to skip binder because the substs on generator types never
         // touch bound regions, they just capture the in-scope
         // type/region parameters
-        let self_ty = *obligation.self_ty().skip_binder();
+        let self_ty = obligation.self_ty();
         match self_ty.sty {
             ty::TyGenerator(..) => {
                 debug!("assemble_generator_candidates: self_ty={:?} obligation={:?}",
@@ -1571,11 +1575,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// parameters and hence do not affect whether this trait is a match or not. They will be
     /// unified during the confirmation step.
     fn assemble_closure_candidates(&mut self,
-                                   obligation: &PolyTraitObligation<'tcx>,
+                                   obligation: &TraitObligation<'tcx>,
                                    candidates: &mut SelectionCandidateSet<'tcx>)
                                    -> Result<(),SelectionError<'tcx>>
     {
-        let kind = match self.tcx().lang_items().fn_trait_kind(obligation.predicate.def_id()) {
+        let kind = match self.tcx().lang_items().fn_trait_kind(obligation.def_id()) {
             Some(k) => k,
             None => { return Ok(()); }
         };
@@ -1583,7 +1587,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // ok to skip binder because the substs on closure types never
         // touch bound regions, they just capture the in-scope
         // type/region parameters
-        match obligation.self_ty().skip_binder().sty {
+        let self_ty = obligation.self_ty();
+        match self_ty.sty {
             ty::TyClosure(closure_def_id, _) => {
                 debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}",
                        kind, obligation);
@@ -1612,17 +1617,17 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Implement one of the `Fn()` family for a fn pointer.
     fn assemble_fn_pointer_candidates(&mut self,
-                                      obligation: &PolyTraitObligation<'tcx>,
+                                      obligation: &TraitObligation<'tcx>,
                                       candidates: &mut SelectionCandidateSet<'tcx>)
                                       -> Result<(),SelectionError<'tcx>>
     {
         // We provide impl of all fn traits for fn pointers.
-        if self.tcx().lang_items().fn_trait_kind(obligation.predicate.def_id()).is_none() {
+        if self.tcx().lang_items().fn_trait_kind(obligation.def_id()).is_none() {
             return Ok(());
         }
 
         // ok to skip binder because what we are inspecting doesn't involve bound regions
-        let self_ty = *obligation.self_ty().skip_binder();
+        let self_ty = obligation.self_ty();
         match self_ty.sty {
             ty::TyInfer(ty::TyVar(_)) => {
                 debug!("assemble_fn_pointer_candidates: ambiguous self-type");
@@ -1649,15 +1654,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Search for impls that might apply to `obligation`.
     fn assemble_candidates_from_impls(&mut self,
-                                      obligation: &PolyTraitObligation<'tcx>,
+                                      obligation: &TraitObligation<'tcx>,
                                       candidates: &mut SelectionCandidateSet<'tcx>)
                                       -> Result<(), SelectionError<'tcx>>
     {
         debug!("assemble_candidates_from_impls(obligation={:?})", obligation);
 
         self.tcx().for_each_relevant_impl(
-            obligation.predicate.def_id(),
-            obligation.predicate.skip_binder().self_ty(),
+            obligation.def_id(),
+            obligation.self_ty(),
             |impl_def_id| {
                 self.probe(|this, _snapshot| {
                     match this.match_impl(impl_def_id, obligation) {
@@ -1674,15 +1679,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn assemble_candidates_from_auto_impls(&mut self,
-                                           obligation: &PolyTraitObligation<'tcx>,
+                                           obligation: &TraitObligation<'tcx>,
                                            candidates: &mut SelectionCandidateSet<'tcx>)
                                            -> Result<(), SelectionError<'tcx>>
     {
-        // OK to skip binder here because the tests we do below do not involve bound regions
-        let self_ty = *obligation.self_ty().skip_binder();
+        let self_ty = obligation.self_ty();
         debug!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty);
 
-        let def_id = obligation.predicate.def_id();
+        let def_id = obligation.def_id();
 
         if self.tcx().trait_is_auto(def_id) {
             match self_ty.sty {
@@ -1729,11 +1733,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Search for impls that might apply to `obligation`.
     fn assemble_candidates_from_object_ty(&mut self,
-                                          obligation: &PolyTraitObligation<'tcx>,
+                                          obligation: &TraitObligation<'tcx>,
                                           candidates: &mut SelectionCandidateSet<'tcx>)
     {
         debug!("assemble_candidates_from_object_ty(self_ty={:?})",
-               obligation.self_ty().skip_binder());
+               obligation.self_ty());
 
         // Object-safety candidates are only applicable to object-safe
         // traits. Including this check is useful because it helps
@@ -1743,7 +1747,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // these cases wind up being considered ambiguous due to a
         // (spurious) ambiguity introduced here.
         let predicate_trait_ref = obligation.predicate;
-        if !self.tcx().is_object_safe(predicate_trait_ref.def_id()) {
+        if !self.tcx().is_object_safe(predicate_trait_ref.def_id) {
             return;
         }
 
@@ -1751,10 +1755,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // the code below doesn't care about regions, and the
             // self-ty here doesn't escape this probe, so just erase
             // any LBR.
-            let self_ty = this.tcx().erase_late_bound_regions(&obligation.self_ty());
+            let self_ty = obligation.self_ty();
             let poly_trait_ref = match self_ty.sty {
                 ty::TyDynamic(ref data, ..) => {
-                    if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
+                    if data.auto_traits().any(|did| did == obligation.def_id()) {
                         debug!("assemble_candidates_from_object_ty: matched builtin bound, \
                                     pushing candidate");
                         candidates.vec.push(BuiltinObjectCandidate);
@@ -1805,30 +1809,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Search for unsizing that might apply to `obligation`.
     fn assemble_candidates_for_unsizing(&mut self,
-                                        obligation: &PolyTraitObligation<'tcx>,
+                                        obligation: &TraitObligation<'tcx>,
                                         candidates: &mut SelectionCandidateSet<'tcx>) {
-        // We currently never consider higher-ranked obligations e.g.
-        // `for<'a> &'a T: Unsize<Trait+'a>` to be implemented. This is not
-        // because they are a priori invalid, and we could potentially add support
-        // for them later, it's just that there isn't really a strong need for it.
-        // A `T: Unsize<U>` obligation is always used as part of a `T: CoerceUnsize<U>`
-        // impl, and those are generally applied to concrete types.
-        //
-        // That said, one might try to write a fn with a where clause like
-        //     for<'a> Foo<'a, T>: Unsize<Foo<'a, Trait>>
-        // where the `'a` is kind of orthogonal to the relevant part of the `Unsize`.
-        // Still, you'd be more likely to write that where clause as
-        //     T: Trait
-        // so it seems ok if we (conservatively) fail to accept that `Unsize`
-        // obligation above. Should be possible to extend this in the future.
-        let source = match self.tcx().no_late_bound_regions(&obligation.self_ty()) {
-            Some(t) => t,
-            None => {
-                // Don't add any candidates if there are bound regions.
-                return;
-            }
-        };
-        let target = obligation.predicate.skip_binder().substs.type_at(1);
+        let source = obligation.self_ty();
+        let target = obligation.predicate.substs.type_at(1);
 
         debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})",
                source, target);
@@ -1982,7 +1966,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             BuiltinImplConditions::Where(nested) => {
                 debug!("builtin_bound: nested={:?}", nested);
                 candidates.vec.push(BuiltinCandidate {
-                    has_nested: nested.skip_binder().len() > 0
+                    has_nested: nested.len() > 0
                 });
                 Ok(())
             }
@@ -1995,14 +1979,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn sized_conditions(&mut self, obligation: &PolyTraitObligation<'tcx>)
+    fn sized_conditions(&mut self, obligation: &TraitObligation<'tcx>)
                      -> BuiltinImplConditions<'tcx>
     {
         use self::BuiltinImplConditions::{Ambiguous, None, Never, Where};
 
-        // NOTE: binder moved to (*)
-        let self_ty = self.infcx.shallow_resolve(
-            obligation.predicate.skip_binder().self_ty());
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
 
         match self_ty.sty {
             ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) |
@@ -2012,21 +1994,18 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             ty::TyArray(..) | ty::TyClosure(..) | ty::TyNever |
             ty::TyError => {
                 // safe for everything
-                Where(ty::Binder(Vec::new()))
+                Where(Vec::new())
             }
 
             ty::TyStr | ty::TySlice(_) | ty::TyDynamic(..) | ty::TyForeign(..) => Never,
 
             ty::TyTuple(tys, _) => {
-                Where(ty::Binder(tys.last().into_iter().cloned().collect()))
+                Where(tys.last().into_iter().cloned().collect())
             }
 
             ty::TyAdt(def, substs) => {
                 let sized_crit = def.sized_constraint(self.tcx());
-                // (*) binder moved here
-                Where(ty::Binder(
-                    sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect()
-                ))
+                Where(sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect())
             }
 
             ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) => None,
@@ -2041,12 +2020,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn copy_clone_conditions(&mut self, obligation: &PolyTraitObligation<'tcx>)
+    fn copy_clone_conditions(&mut self, obligation: &TraitObligation<'tcx>)
                              -> BuiltinImplConditions<'tcx>
     {
-        // NOTE: binder moved to (*)
-        let self_ty = self.infcx.shallow_resolve(
-            obligation.predicate.skip_binder().self_ty());
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
 
         use self::BuiltinImplConditions::{Ambiguous, None, Never, Where};
 
@@ -2056,7 +2033,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar |
             ty::TyRawPtr(..) | ty::TyError | ty::TyNever |
             ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
-                Where(ty::Binder(Vec::new()))
+                Where(Vec::new())
             }
 
             ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) |
@@ -2067,16 +2044,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
             ty::TyArray(element_ty, _) => {
                 // (*) binder moved here
-                Where(ty::Binder(vec![element_ty]))
+                Where(vec![element_ty])
             }
 
             ty::TyTuple(tys, _) => {
                 // (*) binder moved here
-                Where(ty::Binder(tys.to_vec()))
+                Where(tys.to_vec())
             }
 
             ty::TyClosure(def_id, substs) => {
-                let trait_id = obligation.predicate.def_id();
+                let trait_id = obligation.predicate.def_id;
                 let copy_closures =
                     Some(trait_id) == self.tcx().lang_items().copy_trait() &&
                     self.tcx().has_copy_closures(def_id.krate);
@@ -2085,7 +2062,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     self.tcx().has_clone_closures(def_id.krate);
 
                 if copy_closures || clone_closures {
-                    Where(ty::Binder(substs.upvar_tys(def_id, self.tcx()).collect()))
+                    Where(substs.upvar_tys(def_id, self.tcx()).collect())
                 } else {
                     Never
                 }
@@ -2195,50 +2172,38 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
     }
 
+    /// Given a list of types `types` that resulted (typically) from
+    /// an auto trait, create new obligations `T: Foo` for each `T` in
+    /// `types`. So e.g. if we were check that `(A, B): Send`, this
+    /// might result in `A: Send` and `B: Send`.
     fn collect_predicates_for_types(&mut self,
                                     param_env: ty::ParamEnv<'tcx>,
                                     cause: ObligationCause<'tcx>,
                                     recursion_depth: usize,
                                     trait_def_id: DefId,
-                                    types: ty::Binder<Vec<Ty<'tcx>>>)
+                                    types: Vec<Ty<'tcx>>)
                                     -> Vec<PredicateObligation<'tcx>>
     {
-        // Because the types were potentially derived from
-        // higher-ranked obligations they may reference late-bound
-        // regions. For example, `for<'a> Foo<&'a int> : Copy` would
-        // yield a type like `for<'a> &'a int`. In general, we
-        // maintain the invariant that we never manipulate bound
-        // regions, so we have to process these bound regions somehow.
-        //
-        // The strategy is to:
-        //
-        // 1. Instantiate those regions to skolemized regions (e.g.,
-        //    `for<'a> &'a int` becomes `&0 int`.
-        // 2. Produce something like `&'0 int : Copy`
-        // 3. Re-bind the regions back to `for<'a> &'a int : Copy`
-
-        types.skip_binder().into_iter().flat_map(|ty| { // binder moved -\
-            let ty: ty::Binder<Ty<'tcx>> = ty::Binder(ty); // <----------/
-
-            self.in_snapshot(|this, _snapshot| {
-                let (skol_ty, param_env, _skol_map) =
-                    this.infcx().skolemize_late_bound_regions(param_env, &ty);
-                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(param_env,
-                                                       cause.clone(),
-                                                       trait_def_id,
-                                                       recursion_depth,
-                                                       normalized_ty,
-                                                       &[]);
-                obligations.push(skol_obligation);
-                obligations
-            })
+        types.into_iter().flat_map(|ty| {
+            let Normalized { value: normalized_ty, mut obligations } =
+                project::normalize_with_depth(self,
+                                              param_env,
+                                              cause.clone(),
+                                              recursion_depth,
+                                              &ty);
+
+            // in addition to whatever normalize cooked up, add the
+            // obligation that `T: Trait`
+            let addl_obligation =
+                self.tcx().predicate_for_trait_def(param_env,
+                                                   cause.clone(),
+                                                   trait_def_id,
+                                                   recursion_depth,
+                                                   normalized_ty,
+                                                   &[]);
+            obligations.push(addl_obligation);
+
+            obligations
         }).collect()
     }
 
@@ -2250,7 +2215,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     // type error.  See `README.md` for more details.
 
     fn confirm_candidate(&mut self,
-                         obligation: &PolyTraitObligation<'tcx>,
+                         obligation: &TraitObligation<'tcx>,
                          candidate: SelectionCandidate<'tcx>)
                          -> Result<Selection<'tcx>,SelectionError<'tcx>>
     {
@@ -2321,7 +2286,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_projection_candidate(&mut self,
-                                    obligation: &PolyTraitObligation<'tcx>)
+                                    obligation: &TraitObligation<'tcx>)
     {
         self.in_snapshot(|this, _snapshot| {
             let result =
@@ -2331,7 +2296,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_param_candidate(&mut self,
-                               obligation: &PolyTraitObligation<'tcx>,
+                               obligation: &TraitObligation<'tcx>,
                                param: ty::PolyTraitRef<'tcx>)
                                -> Vec<PredicateObligation<'tcx>>
     {
@@ -2354,7 +2319,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_builtin_candidate(&mut self,
-                                 obligation: &PolyTraitObligation<'tcx>,
+                                 obligation: &TraitObligation<'tcx>,
                                  has_nested: bool)
                                  -> VtableBuiltinData<PredicateObligation<'tcx>>
     {
@@ -2363,7 +2328,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let lang_items = self.tcx().lang_items();
         let obligations = if has_nested {
-            let trait_def = obligation.predicate.def_id();
+            let trait_def = obligation.def_id();
             let conditions = match trait_def {
                 _ if Some(trait_def) == lang_items.sized_trait() => {
                     self.sized_conditions(obligation)
@@ -2404,26 +2369,25 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds
     /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds.
     fn confirm_auto_impl_candidate(&mut self,
-                                   obligation: &PolyTraitObligation<'tcx>,
+                                   obligation: &TraitObligation<'tcx>,
                                    trait_def_id: DefId)
-                                   -> VtableAutoImplData<PredicateObligation<'tcx>>
+                                   -> VtableDefaultImplData<PredicateObligation<'tcx>>
     {
         debug!("confirm_auto_impl_candidate({:?}, {:?})",
                obligation,
                trait_def_id);
 
-        // binder is moved below
-        let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
         let types = self.constituent_types_for_ty(self_ty);
-        self.vtable_auto_impl(obligation, trait_def_id, ty::Binder(types))
+        self.vtable_auto_impl(obligation, trait_def_id, types)
     }
 
-    /// See `confirm_auto_impl_candidate`
+    /// See `confirm_default_impl_candidate`
     fn vtable_auto_impl(&mut self,
-                        obligation: &PolyTraitObligation<'tcx>,
+                        obligation: &TraitObligation<'tcx>,
                         trait_def_id: DefId,
-                        nested: ty::Binder<Vec<Ty<'tcx>>>)
-                        -> VtableAutoImplData<PredicateObligation<'tcx>>
+                        nested: Vec<Ty<'tcx>>)
+                        -> VtableDefaultImplData<PredicateObligation<'tcx>>
     {
         debug!("vtable_auto_impl: nested={:?}", nested);
 
@@ -2435,18 +2399,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             trait_def_id,
             nested);
 
-        let trait_obligations = self.in_snapshot(|this, _snapshot| {
-            let poly_trait_ref = obligation.predicate;
-            let (trait_ref, param_env, _skol_map) =
-                this.infcx().skolemize_late_bound_regions(obligation.param_env, &poly_trait_ref);
-            let cause = obligation.derived_cause(ImplDerivedObligation);
-            this.impl_or_trait_obligations(cause,
-                                           obligation.recursion_depth + 1,
-                                           param_env,
-                                           trait_def_id,
-                                           &trait_ref.substs)
-        });
-
+        let trait_ref = obligation.predicate;
+        let cause = obligation.derived_cause(ImplDerivedObligation);
+        let trait_obligations = self.impl_or_trait_obligations(cause,
+                                                               obligation.recursion_depth + 1,
+                                                               obligation.param_env,
+                                                               trait_def_id,
+                                                               &trait_ref.substs);
         obligations.extend(trait_obligations);
 
         debug!("vtable_auto_impl: obligations={:?}", obligations);
@@ -2458,7 +2417,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_impl_candidate(&mut self,
-                              obligation: &PolyTraitObligation<'tcx>,
+                              obligation: &TraitObligation<'tcx>,
                               impl_def_id: DefId)
                               -> VtableImplData<'tcx, PredicateObligation<'tcx>>
     {
@@ -2469,15 +2428,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // First, create the substitutions by matching the impl again,
         // this time not in a probe.
         self.in_snapshot(|this, _snapshot| {
-            let (substs, param_env) =
-                this.rematch_impl(impl_def_id, obligation);
+            let substs = this.rematch_impl(impl_def_id, obligation);
             debug!("confirm_impl_candidate substs={:?}", substs);
             let cause = obligation.derived_cause(ImplDerivedObligation);
             this.vtable_impl(impl_def_id,
                              substs,
                              cause,
                              obligation.recursion_depth + 1,
-                             param_env)
+                             obligation.param_env)
         })
     }
 
@@ -2518,7 +2476,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_object_candidate(&mut self,
-                                obligation: &PolyTraitObligation<'tcx>)
+                                obligation: &TraitObligation<'tcx>)
                                 -> VtableObjectData<'tcx, PredicateObligation<'tcx>>
     {
         debug!("confirm_object_candidate({:?})",
@@ -2528,7 +2486,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // probably flatten the binder from the obligation and the
         // binder from the object. Have to try to make a broken test
         // case that results. -nmatsakis
-        let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
         let poly_trait_ref = match self_ty.sty {
             ty::TyDynamic(ref data, ..) => {
                 data.principal().unwrap().with_self_ty(self.tcx(), self_ty)
@@ -2580,17 +2538,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn confirm_fn_pointer_candidate(&mut self, obligation: &PolyTraitObligation<'tcx>)
+    fn confirm_fn_pointer_candidate(&mut self, obligation: &TraitObligation<'tcx>)
         -> Result<VtableFnPointerData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
     {
         debug!("confirm_fn_pointer_candidate({:?})",
                obligation);
 
-        // ok to skip binder; it is reintroduced below
-        let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
+        let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
         let sig = self_ty.fn_sig(self.tcx());
         let trait_ref =
-            self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
+            self.tcx().closure_trait_ref_and_return_type(obligation.def_id(),
                                                          self_ty,
                                                          sig,
                                                          util::TupleArgumentsFlag::Yes)
@@ -2603,22 +2560,19 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                           obligation.recursion_depth + 1,
                                           &trait_ref);
 
-        self.confirm_poly_trait_refs(obligation.cause.clone(),
-                                     obligation.param_env,
-                                     obligation.predicate,
-                                     trait_ref)?;
+        self.confirm_against_poly_trait_ref(obligation.cause.clone(),
+                                            obligation.param_env,
+                                            obligation.predicate,
+                                            trait_ref)?;
         Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations })
     }
 
     fn confirm_generator_candidate(&mut self,
-                                   obligation: &PolyTraitObligation<'tcx>)
+                                   obligation: &TraitObligation<'tcx>)
                                    -> Result<VtableGeneratorData<'tcx, PredicateObligation<'tcx>>,
                                            SelectionError<'tcx>>
     {
-        // ok to skip binder because the substs on generator types never
-        // touch bound regions, they just capture the in-scope
-        // type/region parameters
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
+        let self_ty = self.infcx.shallow_resolve(&obligation.self_ty());
         let (closure_def_id, substs) = match self_ty.sty {
             ty::TyGenerator(id, substs, _) => (id, substs),
             _ => bug!("closure candidate for non-closure {:?}", obligation)
@@ -2645,10 +2599,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                trait_ref,
                obligations);
 
-        self.confirm_poly_trait_refs(obligation.cause.clone(),
-                                     obligation.param_env,
-                                     obligation.predicate,
-                                     trait_ref)?;
+        self.confirm_against_poly_trait_ref(obligation.cause.clone(),
+                                            obligation.param_env,
+                                            obligation.predicate,
+                                            trait_ref)?;
 
         Ok(VtableGeneratorData {
             closure_def_id: closure_def_id,
@@ -2658,21 +2612,18 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_closure_candidate(&mut self,
-                                 obligation: &PolyTraitObligation<'tcx>)
+                                 obligation: &TraitObligation<'tcx>)
                                  -> Result<VtableClosureData<'tcx, PredicateObligation<'tcx>>,
                                            SelectionError<'tcx>>
     {
         debug!("confirm_closure_candidate({:?})", obligation);
 
-        let kind = match self.tcx().lang_items().fn_trait_kind(obligation.predicate.def_id()) {
+        let kind = match self.tcx().lang_items().fn_trait_kind(obligation.predicate.def_id) {
             Some(k) => k,
             None => bug!("closure candidate for non-fn trait {:?}", obligation)
         };
 
-        // ok to skip binder because the substs on closure types never
-        // touch bound regions, they just capture the in-scope
-        // type/region parameters
-        let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
+        let self_ty = self.infcx.shallow_resolve(&obligation.self_ty());
         let (closure_def_id, substs) = match self_ty.sty {
             ty::TyClosure(id, substs) => (id, substs),
             _ => bug!("closure candidate for non-closure {:?}", obligation)
@@ -2694,10 +2645,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                trait_ref,
                obligations);
 
-        self.confirm_poly_trait_refs(obligation.cause.clone(),
-                                     obligation.param_env,
-                                     obligation.predicate,
-                                     trait_ref)?;
+        self.confirm_against_poly_trait_ref(obligation.cause.clone(),
+                                            obligation.param_env,
+                                            obligation.predicate,
+                                            trait_ref)?;
 
         obligations.push(Obligation::new(
             obligation.cause.clone(),
@@ -2736,32 +2687,29 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// because these output type parameters should not affect the
     /// selection of the impl. Therefore, if there is a mismatch, we
     /// 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>>
+    fn confirm_against_poly_trait_ref(&mut self,
+                                      obligation_cause: ObligationCause<'tcx>,
+                                      obligation_param_env: ty::ParamEnv<'tcx>,
+                                      obligation_trait_ref: ty::TraitRef<'tcx>,
+                                      expected_trait_ref: ty::PolyTraitRef<'tcx>)
+                                      -> Result<(), SelectionError<'tcx>>
     {
         let obligation_trait_ref = obligation_trait_ref.clone();
         self.infcx
             .at(&obligation_cause, obligation_param_env)
-            .sup(obligation_trait_ref, expected_trait_ref)
+            .instantiable_as(expected_trait_ref, obligation_trait_ref)
             .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
             .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e))
     }
 
     fn confirm_builtin_unsize_candidate(&mut self,
-                                        obligation: &PolyTraitObligation<'tcx>,)
+                                        obligation: &TraitObligation<'tcx>,)
         -> Result<VtableBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>>
     {
         let tcx = self.tcx();
 
-        // assemble_candidates_for_unsizing should ensure there are no late bound
-        // regions here. See the comment there for more details.
-        let source = self.infcx.shallow_resolve(
-            tcx.no_late_bound_regions(&obligation.self_ty()).unwrap());
-        let target = obligation.predicate.skip_binder().substs.type_at(1);
+        let source = self.infcx.shallow_resolve(obligation.self_ty());
+        let target = obligation.predicate.substs.type_at(1);
         let target = self.infcx.shallow_resolve(target);
 
         debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})",
@@ -2916,7 +2864,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 nested.push(tcx.predicate_for_trait_def(
                     obligation.param_env,
                     obligation.cause.clone(),
-                    obligation.predicate.def_id(),
+                    obligation.def_id(),
                     obligation.recursion_depth + 1,
                     inner_source,
                     &[inner_target]));
@@ -2947,7 +2895,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 nested.push(tcx.predicate_for_trait_def(
                     obligation.param_env,
                     obligation.cause.clone(),
-                    obligation.predicate.def_id(),
+                    obligation.def_id(),
                     obligation.recursion_depth + 1,
                     a_last,
                     &[b_last]));
@@ -2971,9 +2919,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn rematch_impl(&mut self,
                     impl_def_id: DefId,
-                    obligation: &PolyTraitObligation<'tcx>)
-                    -> (Normalized<'tcx, &'tcx Substs<'tcx>>,
-                        ty::ParamEnv<'tcx>)
+                    obligation: &TraitObligation<'tcx>)
+                    -> Normalized<'tcx, &'tcx Substs<'tcx>>
     {
         match self.match_impl(impl_def_id, obligation) {
             Ok(tuple) => tuple,
@@ -2987,9 +2934,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn match_impl(&mut self,
                   impl_def_id: DefId,
-                  obligation: &PolyTraitObligation<'tcx>)
-                  -> Result<(Normalized<'tcx, &'tcx Substs<'tcx>>,
-                             ty::ParamEnv<'tcx>), ()>
+                  obligation: &TraitObligation<'tcx>)
+                  -> Result<Normalized<'tcx, &'tcx Substs<'tcx>>, ()>
     {
         let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
 
@@ -3000,33 +2946,27 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             return Err(());
         }
 
-        let (skol_obligation_trait_ref, param_env, _skol_map) =
-             self.infcx().skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
-
-        let impl_substs = self.infcx.fresh_substs_for_item(param_env.universe,
+        let impl_substs = self.infcx.fresh_substs_for_item(obligation.param_env.universe,
                                                            obligation.cause.span,
                                                            impl_def_id);
 
-        let impl_trait_ref = impl_trait_ref.subst(self.tcx(),
-                                                  impl_substs);
+        let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs);
 
         let impl_trait_ref =
             project::normalize_with_depth(self,
-                                          param_env,
+                                          obligation.param_env,
                                           obligation.cause.clone(),
                                           obligation.recursion_depth + 1,
                                           &impl_trait_ref);
 
-        debug!("match_impl(impl_def_id={:?}, obligation={:?}, \
-               impl_trait_ref={:?}, skol_obligation_trait_ref={:?})",
+        debug!("match_impl(impl_def_id={:?}, obligation={:?}, impl_trait_ref={:?})",
                impl_def_id,
                obligation,
-               impl_trait_ref,
-               skol_obligation_trait_ref);
+               impl_trait_ref);
 
         let InferOk { obligations, .. } =
-            self.infcx.at(&obligation.cause, param_env)
-                      .eq(skol_obligation_trait_ref, impl_trait_ref.value)
+            self.infcx.at(&obligation.cause, obligation.param_env)
+                      .eq(obligation.predicate, impl_trait_ref.value)
                       .map_err(|e| {
                           debug!("match_impl: failed eq_trait_refs due to `{}`", e);
                           ()
@@ -3034,12 +2974,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         self.inferred_obligations.extend(obligations);
 
         debug!("match_impl: success impl_substs={:?}", impl_substs);
-        Ok((Normalized { value: impl_substs, obligations: impl_trait_ref.obligations },
-            param_env))
+        Ok(Normalized { value: impl_substs, obligations: impl_trait_ref.obligations })
     }
 
     fn fast_reject_trait_refs(&mut self,
-                              obligation: &PolyTraitObligation,
+                              obligation: &TraitObligation,
                               impl_trait_ref: &ty::TraitRef)
                               -> bool
     {
@@ -3047,7 +2986,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         // substitution if we find that any of the input types, when
         // simplified, do not match.
 
-        obligation.predicate.skip_binder().input_types()
+        obligation.predicate.input_types()
             .zip(impl_trait_ref.input_types())
             .any(|(obligation_ty, impl_ty)| {
                 let simplified_obligation_ty =
@@ -3067,7 +3006,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// because where-clauses are stored in the parameter environment
     /// unnormalized.
     fn match_where_clause_trait_ref(&mut self,
-                                    obligation: &PolyTraitObligation<'tcx>,
+                                    obligation: &TraitObligation<'tcx>,
                                     where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
                                     -> Result<Vec<PredicateObligation<'tcx>>,()>
     {
@@ -3078,7 +3017,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// Returns `Ok` if `poly_trait_ref` being true implies that the
     /// obligation is satisfied.
     fn match_poly_trait_ref(&mut self,
-                            obligation: &PolyTraitObligation<'tcx>,
+                            obligation: &TraitObligation<'tcx>,
                             poly_trait_ref: ty::PolyTraitRef<'tcx>)
                             -> Result<(),()>
     {
@@ -3087,7 +3026,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                poly_trait_ref);
 
         self.infcx.at(&obligation.cause, obligation.param_env)
-                  .sup(obligation.predicate, poly_trait_ref)
+                  .instantiable_as(poly_trait_ref, obligation.predicate)
                   .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
                   .map_err(|_| ())
     }
@@ -3096,8 +3035,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     // Miscellany
 
     fn match_fresh_trait_refs(&self,
-                              previous: &ty::PolyTraitRef<'tcx>,
-                              current: &ty::PolyTraitRef<'tcx>)
+                              previous: &ty::TraitRef<'tcx>,
+                              current: &ty::TraitRef<'tcx>)
                               -> bool
     {
         let mut matcher = ty::_match::Match::new(self.tcx());
@@ -3106,7 +3045,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn push_stack<'o,'s:'o>(&mut self,
                             previous_stack: TraitObligationStackList<'s, 'tcx>,
-                            obligation: &'o PolyTraitObligation<'tcx>)
+                            obligation: &'o TraitObligation<'tcx>)
                             -> TraitObligationStack<'o, 'tcx>
     {
         let fresh_trait_ref = obligation.predicate.fold_with(&mut self.freshener);
@@ -3118,7 +3057,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn closure_trait_ref_unnormalized(&mut self,
-                                      obligation: &PolyTraitObligation<'tcx>,
+                                      obligation: &TraitObligation<'tcx>,
                                       closure_def_id: DefId,
                                       substs: ty::ClosureSubsts<'tcx>)
                                       -> ty::PolyTraitRef<'tcx>
@@ -3126,21 +3065,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let closure_type = self.infcx.fn_sig(closure_def_id)
             .subst(self.tcx(), substs.substs);
         let ty::Binder((trait_ref, _)) =
-            self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
-                                                         obligation.predicate.0.self_ty(), // (1)
+            self.tcx().closure_trait_ref_and_return_type(obligation.def_id(),
+                                                         obligation.self_ty(),
                                                          closure_type,
                                                          util::TupleArgumentsFlag::No);
-        // (1) Feels icky to skip the binder here, but OTOH we know
-        // that the self-type is an unboxed closure type and hence is
-        // in fact unparameterized (or at least does not reference any
-        // regions bound in the obligation). Still probably some
-        // refactoring could make this nicer.
 
         ty::Binder(trait_ref)
     }
 
     fn generator_trait_ref_unnormalized(&mut self,
-                                      obligation: &PolyTraitObligation<'tcx>,
+                                      obligation: &TraitObligation<'tcx>,
                                       closure_def_id: DefId,
                                       substs: ty::ClosureSubsts<'tcx>)
                                       -> ty::PolyTraitRef<'tcx>
@@ -3148,14 +3082,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let gen_sig = self.infcx.generator_sig(closure_def_id).unwrap()
             .subst(self.tcx(), substs.substs);
         let ty::Binder((trait_ref, ..)) =
-            self.tcx().generator_trait_ref_and_outputs(obligation.predicate.def_id(),
-                                                       obligation.predicate.0.self_ty(), // (1)
+            self.tcx().generator_trait_ref_and_outputs(obligation.def_id(),
+                                                       obligation.self_ty(),
                                                        gen_sig);
-        // (1) Feels icky to skip the binder here, but OTOH we know
-        // that the self-type is an generator type and hence is
-        // in fact unparameterized (or at least does not reference any
-        // regions bound in the obligation). Still probably some
-        // refactoring could make this nicer.
 
         ty::Binder(trait_ref)
     }
@@ -3205,7 +3134,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 }
 
-impl<'tcx> PolyTraitObligation<'tcx> {
+impl<'tcx> TraitObligation<'tcx> {
     #[allow(unused_comparisons)]
     pub fn derived_cause(&self,
                         variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>)
diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs
index f6db4420090b8..90afb16084dd6 100644
--- a/src/librustc/traits/trans/mod.rs
+++ b/src/librustc/traits/trans/mod.rs
@@ -47,7 +47,7 @@ pub fn trans_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>,
         let obligation_cause = ObligationCause::dummy();
         let obligation = Obligation::new(obligation_cause, param_env, trait_ref);
 
-        let selection = match selcx.select(&obligation) {
+        let selection = match selcx.select_poly(&obligation) {
             Ok(Some(selection)) => selection,
             Ok(None) => {
                 // Ambiguity can happen when monomorphizing during trans
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index e67fc59399034..dc2e2fd7646ac 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1025,7 +1025,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
                 // Attempt to select a concrete impl before checking.
                 ty::TraitContainer(trait_def_id) => {
                     let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, callee_substs);
-                    let trait_ref = ty::Binder(trait_ref);
+                    let trait_ref = trait_ref;
                     let span = tcx.hir.span(expr_id);
                     let obligation =
                         traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 94b41fb9ec694..12650558a6d85 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -550,7 +550,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                     continue;
                 }
             };
-            match selcx.select(&obligation.with(trait_ref)) {
+            match selcx.select_poly(&obligation.with(trait_ref)) {
                 // Uncertain or unimplemented.
                 Ok(None) |
                 Err(traits::Unimplemented) => {
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 1638719a21ef7..59aa427a12c3d 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -19,7 +19,7 @@ use hir::def::Def;
 use namespace::Namespace;
 use rustc::ty::subst::{Subst, Substs};
 use rustc::traits::{self, ObligationCause};
-use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
+use rustc::ty::{self, Ty, ToPredicate, TraitRef, TypeFoldable};
 use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::util::nodemap::FxHashSet;
 use rustc::infer::{self, InferOk};
@@ -957,8 +957,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                               -> traits::SelectionResult<'tcx, traits::Selection<'tcx>>
     {
         let cause = traits::ObligationCause::misc(self.span, self.body_id);
-        let predicate = trait_ref.to_poly_trait_ref();
-        let obligation = traits::Obligation::new(cause, self.param_env, predicate);
+        let obligation = traits::Obligation::new(cause, self.param_env, trait_ref);
         traits::SelectionContext::new(self).select(&obligation)
     }
 
diff --git a/src/test/compile-fail/issue-27942.rs b/src/test/compile-fail/issue-27942.rs
index 0fa4184606e45..42cb42f266953 100644
--- a/src/test/compile-fail/issue-27942.rs
+++ b/src/test/compile-fail/issue-27942.rs
@@ -17,11 +17,11 @@ pub trait Buffer<'a, R: Resources<'a>> {
     fn select(&self) -> BufferViewHandle<R>;
     //~^ ERROR mismatched types
     //~| lifetime mismatch
-    //~| NOTE expected type `Resources<'_>`
+    //~| NOTE expected type `Resources<'a>`
     //~| NOTE ...does not necessarily outlive the anonymous lifetime #1 defined on the method body
     //~| ERROR mismatched types
     //~| lifetime mismatch
-    //~| NOTE expected type `Resources<'_>`
+    //~| NOTE expected type `Resources<'a>`
     //~| NOTE the anonymous lifetime #1 defined on the method body at 17:5...
 }
 

From 0104a6af82e9efed349c1d73f49a780e692a6389 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 6 Sep 2017 11:55:40 -0400
Subject: [PATCH 41/63] remove `ProjectionCandidate` and merge it into bounds
 list

Also, stop storing the precise `PolyTraitRef`. This was needed because
projection candidates wind up incorporating bits of their self-type
(the substitutions, in particular) into the result. Instead just
recreate it during confirmation. This is more to the spirit of how
select was *supposed* to work anyhow.
---
 src/librustc/traits/select.rs        | 460 ++++++++++++++-------------
 src/test/run-pass/use_inline_dtor.rs |   1 +
 2 files changed, 240 insertions(+), 221 deletions(-)

diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 4b47909a70049..820575f64e83f 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -41,6 +41,7 @@ use ty::relate::TypeRelation;
 use middle::lang_items;
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::snapshot_vec::{SnapshotVecDelegate, SnapshotVec};
 use std::iter;
 use std::cell::RefCell;
@@ -144,7 +145,7 @@ struct TraitObligationStack<'prev, 'tcx: 'prev> {
 #[derive(Clone)]
 pub struct SelectionCache<'tcx> {
     hashmap: RefCell<FxHashMap<ty::TraitRef<'tcx>,
-                               WithDepNode<SelectionResult<'tcx, SelectionCandidate<'tcx>>>>>,
+                               WithDepNode<SelectionResult<'tcx, SelectionCandidate>>>>,
 }
 
 /// The selection process begins by considering all impls, where
@@ -219,17 +220,13 @@ pub struct SelectionCache<'tcx> {
 /// required for associated types to work in default impls, as the bounds
 /// are visible both as projection bounds and as where-clauses from the
 /// parameter environment.
-#[derive(PartialEq,Eq,Debug,Clone)]
-enum SelectionCandidate<'tcx> {
+#[derive(PartialEq,Eq,Debug,Copy,Clone)]
+enum SelectionCandidate {
     BuiltinCandidate { has_nested: bool },
-    ParamCandidate(ty::PolyTraitRef<'tcx>),
+    ParamCandidate { bounds_list: BoundsList },
     ImplCandidate(DefId),
     AutoImplCandidate(DefId),
 
-    /// This is a trait matching with a projected type as `Self`, and
-    /// we found an applicable bound in the trait definition.
-    ProjectionCandidate,
-
     /// Implementation of a `Fn`-family trait by one of the anonymous types
     /// generated for a `||` expression.
     ClosureCandidate,
@@ -249,36 +246,35 @@ enum SelectionCandidate<'tcx> {
     BuiltinUnsizeCandidate,
 }
 
-impl<'a, 'tcx> ty::Lift<'tcx> for SelectionCandidate<'a> {
-    type Lifted = SelectionCandidate<'tcx>;
-    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
-        Some(match *self {
-            BuiltinCandidate { has_nested } => {
-                BuiltinCandidate {
-                    has_nested,
-                }
-            }
-            ImplCandidate(def_id) => ImplCandidate(def_id),
-            AutoImplCandidate(def_id) => AutoImplCandidate(def_id),
-            ProjectionCandidate => ProjectionCandidate,
-            FnPointerCandidate => FnPointerCandidate,
-            ObjectCandidate => ObjectCandidate,
-            BuiltinObjectCandidate => BuiltinObjectCandidate,
-            BuiltinUnsizeCandidate => BuiltinUnsizeCandidate,
-            ClosureCandidate => ClosureCandidate,
-            GeneratorCandidate => GeneratorCandidate,
-
-            ParamCandidate(ref trait_ref) => {
-                return tcx.lift(trait_ref).map(ParamCandidate);
-            }
-        })
+impl<'tcx> ty::Lift<'tcx> for SelectionCandidate {
+    type Lifted = SelectionCandidate;
+    fn lift_to_tcx<'b, 'gcx>(&self, _: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        Some(*self)
     }
 }
 
-struct SelectionCandidateSet<'tcx> {
+/// When we prove an obligation via reference to some in-scope where
+/// clause, this enum indicates the source from which we derived that
+/// where-clause.
+///
+/// Note: the ordering is significant. Things later in the list
+/// (somewhat arbitrarily) take precedence over things earlier in the
+/// list.
+#[derive(PartialEq,Eq,PartialOrd,Ord,Debug,Copy,Clone)]
+enum BoundsList {
+    /// The obligation must have a projection as the self-type --
+    /// something like `<T as Foo>::Bar`, in which case the obligation
+    /// was found in the declaration of the `Foo` trait.
+    TraitBounds,
+
+    /// Found in the list of where-clauses in the parameter environment.
+    ParamEnv,
+}
+
+struct SelectionCandidateSet {
     // a list of candidates that definitely apply to the current
     // obligation (meaning: types unify).
-    vec: Vec<SelectionCandidate<'tcx>>,
+    vec: Vec<SelectionCandidate>,
 
     // if this is true, then there were candidates that might or might
     // not have applied, but we couldn't tell. This occurs when some
@@ -288,8 +284,8 @@ struct SelectionCandidateSet<'tcx> {
 }
 
 #[derive(PartialEq,Eq,Debug,Clone)]
-struct EvaluatedCandidate<'tcx> {
-    candidate: SelectionCandidate<'tcx>,
+struct EvaluatedCandidate {
+    candidate: SelectionCandidate,
     evaluation: EvaluationResult,
 }
 
@@ -528,7 +524,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let ret = match self.candidate_from_obligation(&stack)? {
             None => None,
             Some(candidate) => {
-                let mut candidate = self.confirm_candidate(obligation, candidate)?;
+                let mut candidate = self.confirm_candidate(&stack, obligation, candidate)?;
                 let inferred_obligations = (*self.inferred_obligations).into_iter().cloned();
                 candidate.nested_obligations_mut().extend(inferred_obligations);
                 Some(candidate)
@@ -944,14 +940,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// scrutiny.
     fn evaluate_candidate<'o>(&mut self,
                               stack: &TraitObligationStack<'o, 'tcx>,
-                              candidate: &SelectionCandidate<'tcx>)
+                              candidate: &SelectionCandidate)
                               -> EvaluationResult
     {
         debug!("evaluate_candidate: depth={} candidate={:?}",
                stack.obligation.recursion_depth, candidate);
         let result = self.probe(|this, _| {
             let candidate = (*candidate).clone();
-            match this.confirm_candidate(stack.obligation, candidate) {
+            match this.confirm_candidate(stack, stack.obligation, candidate) {
                 Ok(selection) => {
                     this.evaluate_predicates_recursively(
                         stack.list(),
@@ -1018,7 +1014,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn candidate_from_obligation<'o>(&mut self,
                                      stack: &TraitObligationStack<'o, 'tcx>)
-                                     -> SelectionResult<'tcx, SelectionCandidate<'tcx>>
+                                     -> SelectionResult<'tcx, SelectionCandidate>
     {
         // Watch out for overflow. This intentionally bypasses (and does
         // not update) the cache.
@@ -1073,8 +1069,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     // Treat negative impls as unimplemented
-    fn filter_negative_impls(&self, candidate: SelectionCandidate<'tcx>)
-                             -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+    fn filter_negative_impls(&self, candidate: SelectionCandidate)
+                             -> SelectionResult<'tcx, SelectionCandidate> {
         if let ImplCandidate(def_id) = candidate {
             if self.tcx().impl_polarity(def_id) == hir::ImplPolarity::Negative {
                 return Err(Unimplemented)
@@ -1085,7 +1081,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn candidate_from_obligation_no_cache<'o>(&mut self,
                                               stack: &TraitObligationStack<'o, 'tcx>)
-                                              -> SelectionResult<'tcx, SelectionCandidate<'tcx>>
+                                              -> SelectionResult<'tcx, SelectionCandidate>
     {
         if stack.obligation.predicate.references_error() {
             // If we encounter a `TyError`, we generally prefer the
@@ -1272,7 +1268,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn check_candidate_cache(&mut self,
                              param_env: ty::ParamEnv<'tcx>,
                              cache_fresh_trait_pred: ty::TraitRef<'tcx>)
-                             -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>>
+                             -> Option<SelectionResult<'tcx, SelectionCandidate>>
     {
         let tcx = self.tcx();
         if self.can_use_global_caches(param_env) {
@@ -1291,7 +1287,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                               param_env: ty::ParamEnv<'tcx>,
                               cache_fresh_trait_pred: ty::TraitRef<'tcx>,
                               dep_node: DepNodeIndex,
-                              candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>)
+                              candidate: SelectionResult<'tcx, SelectionCandidate>)
     {
         let tcx = self.tcx();
         if self.can_use_global_caches(param_env) {
@@ -1312,7 +1308,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn assemble_candidates<'o>(&mut self,
                                stack: &TraitObligationStack<'o, 'tcx>)
-                               -> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>>
+                               -> Result<SelectionCandidateSet, SelectionError<'tcx>>
     {
         let TraitObligationStack { obligation, .. } = *stack;
         let ref obligation = Obligation {
@@ -1379,8 +1375,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
              self.assemble_candidates_from_object_ty(obligation, &mut candidates);
         }
 
-        self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
-        self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?;
+        self.assemble_candidates_from_projected_tys(obligation, stack, &mut candidates)?;
+        self.assemble_candidates_from_caller_bounds(obligation, stack, &mut candidates)?;
         // Auto implementations have lower priority, so we only
         // consider triggering a default if there is no other impl that can apply.
         if candidates.vec.is_empty() {
@@ -1390,102 +1386,39 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         Ok(candidates)
     }
 
-    fn assemble_candidates_from_projected_tys(&mut self,
-                                              obligation: &TraitObligation<'tcx>,
-                                              candidates: &mut SelectionCandidateSet<'tcx>)
+    /// Finds candidates where the thing we are trying to prove looks like
+    ///
+    ///    <T as Trait>::Foo: Bar
+    ///
+    /// by looking in the definition of `Trait` to see if the
+    /// associated type `Foo` includes a `Bar` bound:
+    ///
+    ///     trait Trait {
+    ///         type Foo: Bar;
+    ///     }
+    ///
+    /// If so, this will be encoded by a predicate on the trait like
+    /// `<Self as Trait>::Foo: Bar`, so what we do is to find the
+    /// trait predicates, substitute appropriately, and then check if
+    /// our obligation goal is found in there.
+    ///
+    /// # Parameters
+    ///
+    /// - `obligation` -- top of stack, but with type variables resolved
+    /// - `stack` -- evaluation stack
+    /// - `candidates` -- list where to push candidates
+    fn assemble_candidates_from_projected_tys<'o>(&mut self,
+                                                  obligation: &TraitObligation<'tcx>,
+                                                  stack: &TraitObligationStack<'o, 'tcx>,
+                                                  candidates: &mut SelectionCandidateSet)
+                                                  -> Result<(),SelectionError<'tcx>>
     {
         debug!("assemble_candidates_for_projected_tys({:?})", obligation);
 
-        // before we go into the whole skolemization thing, just
-        // quickly check if the self-type is a projection at all.
-        match obligation.self_ty().sty {
-            ty::TyProjection(_) | ty::TyAnon(..) => {}
-            ty::TyInfer(ty::TyVar(_)) => {
-                span_bug!(obligation.cause.span,
-                    "Self=_ should have been handled by assemble_candidates");
-            }
-            _ => return
-        }
-
-        let result = self.probe(|this, _snapshot| {
-            this.match_projection_obligation_against_definition_bounds(obligation)
-        });
-
-        if result {
-            candidates.vec.push(ProjectionCandidate);
-        }
-    }
-
-    fn match_projection_obligation_against_definition_bounds(
-        &mut self,
-        obligation: &TraitObligation<'tcx>)
-        -> bool
-    {
-        let trait_ref = self.infcx().resolve_type_vars_if_possible(&obligation.predicate);
-        debug!("match_projection_obligation_against_definition_bounds: trait_ref={:?}",
-               trait_ref);
-
-        let (def_id, substs) = match trait_ref.self_ty().sty {
-            ty::TyProjection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs),
-            ty::TyAnon(def_id, substs) => (def_id, substs),
-            _ => {
-                span_bug!(
-                    obligation.cause.span,
-                    "match_projection_obligation_against_definition_bounds() called \
-                     but self-ty not a projection: {:?}",
-                    trait_ref.self_ty());
-            }
-        };
-        debug!("match_projection_obligation_against_definition_bounds: def_id={:?}, substs={:?}",
-               def_id, substs);
-
-        let predicates_of = self.tcx().predicates_of(def_id);
-        let bounds = predicates_of.instantiate(self.tcx(), substs);
-        debug!("match_projection_obligation_against_definition_bounds: bounds={:?}",
-               bounds);
-
-        let matching_bound =
-            util::elaborate_predicates(self.tcx(), bounds.predicates)
-            .filter_to_traits()
-            .find(
-                |bound| self.probe(
-                    |this, _| this.match_projection(obligation,
-                                                    bound.clone(),
-                                                    trait_ref)));
-
-        debug!("match_projection_obligation_against_definition_bounds: \
-                matching_bound={:?}",
-               matching_bound);
-        match matching_bound {
-            None => false,
-            Some(bound) => {
-                // Repeat the successful match, if any, this time outside of a probe.
-                let result = self.match_projection(obligation,
-                                                   bound,
-                                                   trait_ref);
-                assert!(result);
-                true
-            }
-        }
-    }
-
-    fn match_projection(&mut self,
-                        obligation: &TraitObligation<'tcx>,
-                        trait_bound: ty::PolyTraitRef<'tcx>,
-                        skol_trait_ref: ty::TraitRef<'tcx>)
-                        -> bool
-    {
-        trait_bound.def_id() == skol_trait_ref.def_id && {
-            assert!(!skol_trait_ref.has_escaping_regions());
-            match self.infcx.at(&obligation.cause, obligation.param_env)
-                            .instantiable_as(trait_bound, skol_trait_ref) {
-                                Ok(InferOk { obligations, .. }) => {
-                                    self.inferred_obligations.extend(obligations);
-                                    true
-                                }
-                                Err(_) => false,
-                            }
-        }
+        self.assemble_candidates_from_bounds_list(obligation,
+                                                  stack,
+                                                  candidates,
+                                                  BoundsList::TraitBounds)
     }
 
     /// Given an obligation like `<SomeTrait for T>`, search the obligations that the caller
@@ -1493,31 +1426,46 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     ///
     /// Never affects inference environment.
     fn assemble_candidates_from_caller_bounds<'o>(&mut self,
+                                                  obligation: &TraitObligation<'tcx>,
                                                   stack: &TraitObligationStack<'o, 'tcx>,
-                                                  candidates: &mut SelectionCandidateSet<'tcx>)
+                                                  candidates: &mut SelectionCandidateSet)
                                                   -> Result<(),SelectionError<'tcx>>
     {
         debug!("assemble_candidates_from_caller_bounds({:?})",
                stack.obligation);
 
-        let all_bounds =
-            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.
-        let matching_bounds =
-            all_bounds.filter(|p| p.def_id() == stack.obligation.def_id());
-
-        let matching_bounds =
-            matching_bounds.filter(
-                |bound| self.evaluate_where_clause(stack, bound.clone()).may_apply());
+        self.assemble_candidates_from_bounds_list(obligation,
+                                                  stack,
+                                                  candidates,
+                                                  BoundsList::ParamEnv)
+    }
 
-        let param_candidates =
-            matching_bounds.map(|bound| ParamCandidate(bound));
+    /// Given an obligation `O` like `<SomeTrait for T>`, search the
+    /// given list of bounds (which must be where-clauses that have
+    /// been proven elsewhere; e.g., by the caller) to find out
+    /// whether `O` is listed among them.
+    ///
+    /// Never affects inference environment.
+    fn assemble_candidates_from_bounds_list<'o>(&mut self,
+                                                obligation: &TraitObligation<'tcx>,
+                                                stack: &TraitObligationStack<'o, 'tcx>,
+                                                candidates: &mut SelectionCandidateSet,
+                                                bounds_list: BoundsList)
+                                                -> Result<(),SelectionError<'tcx>>
+    {
+        let mut bounds = self.bounds_list_iterator(stack, obligation, bounds_list);
 
-        candidates.vec.extend(param_candidates);
+        // Final step: If there are any matches, transform into a candidate.
+        if bounds.next().is_some() {
+            // at least one match found...
+            if bounds.next().is_some() {
+                // ...at least two matches found...
+                candidates.ambiguous = true;
+            } else {
+                // ...exactly one match found.
+                candidates.vec.push(ParamCandidate { bounds_list });
+            }
+        }
 
         Ok(())
     }
@@ -1539,7 +1487,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn assemble_generator_candidates(&mut self,
                                    obligation: &TraitObligation<'tcx>,
-                                   candidates: &mut SelectionCandidateSet<'tcx>)
+                                   candidates: &mut SelectionCandidateSet)
                                    -> Result<(),SelectionError<'tcx>>
     {
         if self.tcx().lang_items().gen_trait() != Some(obligation.def_id()) {
@@ -1576,7 +1524,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// unified during the confirmation step.
     fn assemble_closure_candidates(&mut self,
                                    obligation: &TraitObligation<'tcx>,
-                                   candidates: &mut SelectionCandidateSet<'tcx>)
+                                   candidates: &mut SelectionCandidateSet)
                                    -> Result<(),SelectionError<'tcx>>
     {
         let kind = match self.tcx().lang_items().fn_trait_kind(obligation.def_id()) {
@@ -1618,7 +1566,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// Implement one of the `Fn()` family for a fn pointer.
     fn assemble_fn_pointer_candidates(&mut self,
                                       obligation: &TraitObligation<'tcx>,
-                                      candidates: &mut SelectionCandidateSet<'tcx>)
+                                      candidates: &mut SelectionCandidateSet)
                                       -> Result<(),SelectionError<'tcx>>
     {
         // We provide impl of all fn traits for fn pointers.
@@ -1655,7 +1603,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// Search for impls that might apply to `obligation`.
     fn assemble_candidates_from_impls(&mut self,
                                       obligation: &TraitObligation<'tcx>,
-                                      candidates: &mut SelectionCandidateSet<'tcx>)
+                                      candidates: &mut SelectionCandidateSet)
                                       -> Result<(), SelectionError<'tcx>>
     {
         debug!("assemble_candidates_from_impls(obligation={:?})", obligation);
@@ -1680,7 +1628,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn assemble_candidates_from_auto_impls(&mut self,
                                            obligation: &TraitObligation<'tcx>,
-                                           candidates: &mut SelectionCandidateSet<'tcx>)
+                                           candidates: &mut SelectionCandidateSet)
                                            -> Result<(), SelectionError<'tcx>>
     {
         let self_ty = obligation.self_ty();
@@ -1734,7 +1682,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// Search for impls that might apply to `obligation`.
     fn assemble_candidates_from_object_ty(&mut self,
                                           obligation: &TraitObligation<'tcx>,
-                                          candidates: &mut SelectionCandidateSet<'tcx>)
+                                          candidates: &mut SelectionCandidateSet)
     {
         debug!("assemble_candidates_from_object_ty(self_ty={:?})",
                obligation.self_ty());
@@ -1810,7 +1758,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// Search for unsizing that might apply to `obligation`.
     fn assemble_candidates_for_unsizing(&mut self,
                                         obligation: &TraitObligation<'tcx>,
-                                        candidates: &mut SelectionCandidateSet<'tcx>) {
+                                        candidates: &mut SelectionCandidateSet) {
         let source = obligation.self_ty();
         let target = obligation.predicate.substs.type_at(1);
 
@@ -1893,8 +1841,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// See the comment for "SelectionCandidate" for more details.
     fn candidate_should_be_dropped_in_favor_of<'o>(
         &mut self,
-        victim: &EvaluatedCandidate<'tcx>,
-        other: &EvaluatedCandidate<'tcx>)
+        victim: &EvaluatedCandidate,
+        other: &EvaluatedCandidate)
         -> bool
     {
         if victim.candidate == other.candidate {
@@ -1902,31 +1850,43 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
 
         match other.candidate {
-            ObjectCandidate |
-            ParamCandidate(_) | ProjectionCandidate => match victim.candidate {
-                AutoImplCandidate(..) => {
-                    bug!(
-                        "default implementations shouldn't be recorded \
-                         when there are other valid candidates");
-                }
-                ImplCandidate(..) |
-                ClosureCandidate |
-                GeneratorCandidate |
-                FnPointerCandidate |
-                BuiltinObjectCandidate |
-                BuiltinUnsizeCandidate |
-                BuiltinCandidate { .. } => {
-                    // We have a where-clause so don't go around looking
-                    // for impls.
-                    true
+            ObjectCandidate | ParamCandidate { .. } if other.evaluation == EvaluatedToOk => {
+                match victim.candidate {
+                    AutoImplCandidate(..) => {
+                        bug!(
+                            "auto implementations shouldn't be recorded \
+                             when there are other valid candidates");
+                    }
+                    ImplCandidate(..) |
+                    ClosureCandidate |
+                    GeneratorCandidate |
+                    FnPointerCandidate |
+                    BuiltinObjectCandidate |
+                    BuiltinUnsizeCandidate |
+                    BuiltinCandidate { .. } => {
+                        // We have a where-clause so don't go around looking
+                        // for impls.
+                        true
+                    }
+                    ObjectCandidate => {
+                        // Arbitrarily give param candidates priority
+                        // over object candidates.
+                        true
+                    },
+                    ParamCandidate { bounds_list: victim_bounds_list, .. } => {
+                        // The "other" must have been either an object
+                        // or a param candidate.  If it was an object
+                        // candidate, it does not take precedence,
+                        // since we rank param higher. If it was
+                        // another param candidate, compare the bounds
+                        // list precedence.
+                        match other.candidate {
+                            ObjectCandidate => return false,
+                            ParamCandidate { bounds_list, .. } => bounds_list > victim_bounds_list,
+                            _ => bug!("other not object nor param: {:?}", other.candidate)
+                        }
+                    }
                 }
-                ObjectCandidate |
-                ProjectionCandidate => {
-                    // Arbitrarily give param candidates priority
-                    // over projection and object candidates.
-                    true
-                },
-                ParamCandidate(..) => false,
             },
             ImplCandidate(other_def) => {
                 // See if we can toss out `victim` based on specialization.
@@ -1959,7 +1919,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     // other impls.
     fn assemble_builtin_bound_candidates<'o>(&mut self,
                                              conditions: BuiltinImplConditions<'tcx>,
-                                             candidates: &mut SelectionCandidateSet<'tcx>)
+                                             candidates: &mut SelectionCandidateSet)
                                              -> Result<(),SelectionError<'tcx>>
     {
         match conditions {
@@ -2214,10 +2174,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     // with the values found in the obligation, possibly yielding a
     // type error.  See `README.md` for more details.
 
-    fn confirm_candidate(&mut self,
-                         obligation: &TraitObligation<'tcx>,
-                         candidate: SelectionCandidate<'tcx>)
-                         -> Result<Selection<'tcx>,SelectionError<'tcx>>
+    fn confirm_candidate<'o>(&mut self,
+                             stack: &TraitObligationStack<'o, 'tcx>,
+                             obligation: &TraitObligation<'tcx>,
+                             candidate: SelectionCandidate)
+                             -> Result<Selection<'tcx>,SelectionError<'tcx>>
     {
         debug!("confirm_candidate({:?}, {:?})",
                obligation,
@@ -2229,8 +2190,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 Ok(VtableBuiltin(data))
             }
 
-            ParamCandidate(param) => {
-                let obligations = self.confirm_param_candidate(obligation, param);
+            ParamCandidate { bounds_list } => {
+                let obligations = self.confirm_param_candidate(stack, obligation, bounds_list);
                 Ok(VtableParam(obligations))
             }
 
@@ -2273,11 +2234,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 Ok(VtableFnPointer(data))
             }
 
-            ProjectionCandidate => {
-                self.confirm_projection_candidate(obligation);
-                Ok(VtableParam(Vec::new()))
-            }
-
             BuiltinUnsizeCandidate => {
                 let data = self.confirm_builtin_unsize_candidate(obligation)?;
                 Ok(VtableBuiltin(data))
@@ -2285,35 +2241,47 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn confirm_projection_candidate(&mut self,
-                                    obligation: &TraitObligation<'tcx>)
-    {
-        self.in_snapshot(|this, _snapshot| {
-            let result =
-                this.match_projection_obligation_against_definition_bounds(obligation);
-            assert!(result);
-        })
-    }
-
-    fn confirm_param_candidate(&mut self,
-                               obligation: &TraitObligation<'tcx>,
-                               param: ty::PolyTraitRef<'tcx>)
-                               -> Vec<PredicateObligation<'tcx>>
+    fn confirm_param_candidate<'o>(&mut self,
+                                   stack: &TraitObligationStack<'o, 'tcx>,
+                                   obligation: &TraitObligation<'tcx>,
+                                   bounds_list: BoundsList)
+                                   -> Vec<PredicateObligation<'tcx>>
     {
         debug!("confirm_param_candidate({:?},{:?})",
                obligation,
-               param);
+               bounds_list);
+
+        // We need a fully resolved predicate for
+        // `bounds_list_iterator`, and I don't know that `obligation`
+        // necessarily is.
+        let 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)
+        };
+
+        let where_clause_trait_ref = {
+            let mut bounds = self.bounds_list_iterator(stack, obligation, bounds_list);
+            bounds.next().unwrap_or_else(|| {
+                span_bug!(obligation.cause.span,
+                          "bounds list {:?} no longer has any matching entries",
+                          bounds_list);
+            })
+        };
 
         // During evaluation, we already checked that this
         // where-clause trait-ref could be unified with the obligation
         // trait-ref. Repeat that unification now without any
         // transactional boundary; it should not fail.
-        match self.match_where_clause_trait_ref(obligation, param.clone()) {
+        match self.match_where_clause_trait_ref(obligation, where_clause_trait_ref) {
             Ok(obligations) => obligations,
             Err(()) => {
-                bug!("Where clause `{:?}` was applicable to `{:?}` but now is not",
-                     param,
-                     obligation);
+                span_bug!(
+                    obligation.cause.span,
+                    "Where clause `{:?}` was applicable to `{:?}` but now is not",
+                    where_clause_trait_ref,
+                    obligation);
             }
         }
     }
@@ -3132,6 +3100,56 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 }))
         }).collect()
     }
+
+    /// Create the bounds list from the given source for the given obligation.
+    /// `obligation` is expected to have type variables resolved.
+    fn bounds_list_iterator<'a>(&'a mut self,
+                                stack: &'a TraitObligationStack<'a, 'tcx>,
+                                obligation: &TraitObligation<'tcx>,
+                                bounds_list: BoundsList)
+                                -> Box<Iterator<Item = ty::PolyTraitRef<'tcx>> + 'a>
+    where 'cx: 'a {
+        let bounds: Box<Iterator<Item = ty::Predicate<'tcx>>> = match bounds_list {
+            BoundsList::ParamEnv =>
+                Box::new(obligation.param_env.caller_bounds.into_iter().cloned()),
+
+            BoundsList::TraitBounds => {
+                // Identify the trait from which `Self` is a
+                // projection (if any), along with its substitutions:
+                let self_ty = obligation.self_ty();
+                let (def_id, substs) = match self_ty.sty {
+                    ty::TyProjection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs),
+                    ty::TyAnon(def_id, substs) => (def_id, substs),
+
+                    // Not a projection? Return empty bounds list.
+                    _ => return Box::new(None.into_iter()),
+                };
+
+                debug!("bounds_list_iterator: def_id={:?}, substs={:?}",
+                       def_id, substs);
+
+                let predicates_of = self.tcx().predicates_of(def_id);
+                let bounds = predicates_of.instantiate(self.tcx(), substs);
+                debug!("bounds_list_iterator: bounds={:?}", bounds);
+
+                Box::new(util::elaborate_predicates(self.tcx(), bounds.predicates))
+            }
+        };
+
+        // Filter down to trait-ref bounds:
+        let bounds = bounds.filter_map(move |o| o.to_opt_poly_trait_ref());
+
+        // Micro-optimization: filter out predicates relating to different
+        // traits.
+        let bounds = bounds.filter(move |&p| p.def_id() == stack.obligation.def_id());
+
+        // Remove duplicates.
+        let mut dedup = FxHashSet();
+        let bounds = bounds.filter(move |&p| dedup.insert(p));
+
+        // Keep only those that may apply
+        Box::new(bounds.filter(move |&p| self.evaluate_where_clause(stack, p).may_apply()))
+    }
 }
 
 impl<'tcx> TraitObligation<'tcx> {
diff --git a/src/test/run-pass/use_inline_dtor.rs b/src/test/run-pass/use_inline_dtor.rs
index 0f55a357a00f4..64155249993b0 100644
--- a/src/test/run-pass/use_inline_dtor.rs
+++ b/src/test/run-pass/use_inline_dtor.rs
@@ -1,3 +1,4 @@
+
 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.

From 2ae8eba0d906d2c8a574644ae37cac23b297e337 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 7 Sep 2017 09:01:06 -0400
Subject: [PATCH 42/63] incorporate param-env into caching

This is a naive integration -- it will lead to less effective caching
than it could because we don't adjust the universe index. However, it
also fixes a pre-existing bug -- in particular, we could wind up
storing a cache entry that is specific to a param env in the local
cache; evaluation then clears the param env, but still hits the local
cache. This defeats the purpose of having evaluation clear the param
env.
---
 src/librustc/traits/select.rs | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 820575f64e83f..b781d58aa7955 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -144,7 +144,7 @@ struct TraitObligationStack<'prev, 'tcx: 'prev> {
 
 #[derive(Clone)]
 pub struct SelectionCache<'tcx> {
-    hashmap: RefCell<FxHashMap<ty::TraitRef<'tcx>,
+    hashmap: RefCell<FxHashMap<CacheKey<'tcx>,
                                WithDepNode<SelectionResult<'tcx, SelectionCandidate>>>>,
 }
 
@@ -404,9 +404,11 @@ impl EvaluationResult {
 
 #[derive(Clone)]
 pub struct EvaluationCache<'tcx> {
-    hashmap: RefCell<FxHashMap<ty::TraitRef<'tcx>, WithDepNode<EvaluationResult>>>
+    hashmap: RefCell<FxHashMap<CacheKey<'tcx>, WithDepNode<EvaluationResult>>>
 }
 
+type CacheKey<'tcx> = (ty::TraitRef<'tcx>, ty::ParamEnv<'tcx>);
+
 impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     pub fn new(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>) -> SelectionContext<'cx, 'gcx, 'tcx> {
         SelectionContext {
@@ -516,7 +518,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// type environment by performing unification.
     pub fn select(&mut self, obligation: &TraitObligation<'tcx>)
                   -> SelectionResult<'tcx, Selection<'tcx>> {
-        debug!("select({:?})", obligation);
+        debug!("select({:?}, param_env={:?})", obligation, obligation.param_env);
 
         let tcx = self.tcx();
 
@@ -969,13 +971,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let tcx = self.tcx();
         if self.can_use_global_caches(param_env) {
             let cache = tcx.evaluation_cache.hashmap.borrow();
-            if let Some(cached) = cache.get(&trait_ref) {
+            if let Some(cached) = cache.get(&(trait_ref, param_env)) {
                 return Some(cached.get(tcx));
             }
         }
         self.infcx.evaluation_cache.hashmap
                                    .borrow()
-                                   .get(&trait_ref)
+                                   .get(&(trait_ref, param_env))
                                    .map(|v| v.get(tcx))
     }
 
@@ -993,15 +995,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         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, WithDepNode::new(dep_node, result));
+            if let Some(key) = self.tcx().lift_to_global(&(trait_ref, param_env)) {
+                cache.insert(key, WithDepNode::new(dep_node, result));
                 return;
             }
         }
 
         self.infcx.evaluation_cache.hashmap
                                    .borrow_mut()
-                                   .insert(trait_ref, WithDepNode::new(dep_node, result));
+                                   .insert((trait_ref, param_env),
+                                           WithDepNode::new(dep_node, result));
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -1273,13 +1276,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let tcx = self.tcx();
         if self.can_use_global_caches(param_env) {
             let cache = tcx.selection_cache.hashmap.borrow();
-            if let Some(cached) = cache.get(&cache_fresh_trait_pred) {
+            if let Some(cached) = cache.get(&(cache_fresh_trait_pred, param_env)) {
                 return Some(cached.get(tcx));
             }
         }
         self.infcx.selection_cache.hashmap
                                   .borrow()
-                                  .get(&cache_fresh_trait_pred)
+                                  .get(&(cache_fresh_trait_pred, param_env))
                                   .map(|v| v.get(tcx))
     }
 
@@ -1292,17 +1295,22 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let tcx = self.tcx();
         if self.can_use_global_caches(param_env) {
             let mut cache = tcx.selection_cache.hashmap.borrow_mut();
-            if let Some(cache_fresh_trait_pred) = tcx.lift_to_global(&cache_fresh_trait_pred) {
+            if let Some(key) = tcx.lift_to_global(&(cache_fresh_trait_pred, param_env)) {
                 if let Some(candidate) = tcx.lift_to_global(&candidate) {
-                    cache.insert(cache_fresh_trait_pred, WithDepNode::new(dep_node, candidate));
+                    debug!("insert_candidate_cache: inserting into global cache {:?} => {:?}",
+                           cache_fresh_trait_pred, candidate);
+                    cache.insert(key, WithDepNode::new(dep_node, candidate));
                     return;
                 }
             }
         }
 
+        debug!("insert_candidate_cache: inserting into local cache {:?} => {:?}",
+               cache_fresh_trait_pred, candidate);
+
         self.infcx.selection_cache.hashmap
                                   .borrow_mut()
-                                  .insert(cache_fresh_trait_pred,
+                                  .insert((cache_fresh_trait_pred, param_env),
                                           WithDepNode::new(dep_node, candidate));
     }
 

From a87accf4b4a147c2377fc50345ed574ccc9be46e Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 7 Sep 2017 13:12:26 -0400
Subject: [PATCH 43/63] kill inferred obligations

---
 src/librustc/traits/mod.rs    |  13 ----
 src/librustc/traits/select.rs | 118 +++++++++++++---------------------
 2 files changed, 46 insertions(+), 85 deletions(-)

diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 70793f72bae7d..b370475f993b5 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -784,19 +784,6 @@ impl<'tcx, N> Vtable<'tcx, N> {
         }
     }
 
-    fn nested_obligations_mut(&mut self) -> &mut Vec<N> {
-        match self {
-            &mut VtableImpl(ref mut i) => &mut i.nested,
-            &mut VtableParam(ref mut n) => n,
-            &mut VtableBuiltin(ref mut i) => &mut i.nested,
-            &mut VtableAutoImpl(ref mut d) => &mut d.nested,
-            &mut VtableGenerator(ref mut c) => &mut c.nested,
-            &mut VtableClosure(ref mut c) => &mut c.nested,
-            &mut VtableObject(ref mut d) => &mut d.nested,
-            &mut VtableFnPointer(ref mut d) => &mut d.nested,
-        }
-    }
-
     pub fn map<M, F>(self, f: F) -> Vtable<'tcx, M> where F: FnMut(N) -> M {
         match self {
             VtableImpl(i) => VtableImpl(VtableImplData {
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index b781d58aa7955..f3a80529505c4 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -42,27 +42,16 @@ use middle::lang_items;
 
 use rustc_data_structures::bitvec::BitVector;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::snapshot_vec::{SnapshotVecDelegate, SnapshotVec};
 use std::iter;
 use std::cell::RefCell;
 use std::cmp;
 use std::fmt;
-use std::marker::PhantomData;
 use std::rc::Rc;
 use syntax::abi::Abi;
 use hir;
 use lint;
 use util::nodemap::FxHashMap;
 
-struct InferredObligationsSnapshotVecDelegate<'tcx> {
-    phantom: PhantomData<&'tcx i32>,
-}
-impl<'tcx> SnapshotVecDelegate for InferredObligationsSnapshotVecDelegate<'tcx> {
-    type Value = PredicateObligation<'tcx>;
-    type Undo = ();
-    fn reverse(_: &mut Vec<Self::Value>, _: Self::Undo) {}
-}
-
 pub struct SelectionContext<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
     infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
 
@@ -89,8 +78,6 @@ pub struct SelectionContext<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
     /// would satisfy it. This avoids crippling inference, basically.
     intercrate: bool,
 
-    inferred_obligations: SnapshotVec<InferredObligationsSnapshotVecDelegate<'tcx>>,
-
     intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
 }
 
@@ -415,7 +402,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             infcx,
             freshener: infcx.freshener(),
             intercrate: false,
-            inferred_obligations: SnapshotVec::new(),
             intercrate_ambiguity_causes: Vec::new(),
         }
     }
@@ -425,7 +411,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             infcx,
             freshener: infcx.freshener(),
             intercrate: true,
-            inferred_obligations: SnapshotVec::new(),
             intercrate_ambiguity_causes: Vec::new(),
         }
     }
@@ -451,8 +436,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn in_snapshot<R, F>(&mut self, f: F) -> R
         where F: FnOnce(&mut Self, &infer::CombinedSnapshot<'cx, 'tcx>) -> R
     {
-        // The irrefutable nature of the operation means we don't need to snapshot the
-        // inferred_obligations vector.
         self.infcx.in_snapshot(|snapshot| f(self, snapshot))
     }
 
@@ -461,10 +444,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn probe<R, F>(&mut self, f: F) -> R
         where F: FnOnce(&mut Self, &infer::CombinedSnapshot<'cx, 'tcx>) -> R
     {
-        let inferred_obligations_snapshot = self.inferred_obligations.start_snapshot();
-        let result = self.infcx.probe(|snapshot| f(self, snapshot));
-        self.inferred_obligations.rollback_to(inferred_obligations_snapshot);
-        result
+        self.infcx.probe(|snapshot| f(self, snapshot))
     }
 
     /// Wraps a commit_if_ok s.t. obligations collected during it are not returned in selection if
@@ -472,17 +452,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn commit_if_ok<T, E, F>(&mut self, f: F) -> Result<T, E> where
         F: FnOnce(&mut Self, &infer::CombinedSnapshot) -> Result<T, E>
     {
-        let inferred_obligations_snapshot = self.inferred_obligations.start_snapshot();
-        match self.infcx.commit_if_ok(|snapshot| f(self, snapshot)) {
-            Ok(ok) => {
-                self.inferred_obligations.commit(inferred_obligations_snapshot);
-                Ok(ok)
-            },
-            Err(err) => {
-                self.inferred_obligations.rollback_to(inferred_obligations_snapshot);
-                Err(err)
-            }
-        }
+        self.infcx.commit_if_ok(|snapshot| f(self, snapshot))
     }
 
 
@@ -525,12 +495,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let stack = self.push_stack(TraitObligationStackList::empty(), obligation);
         let ret = match self.candidate_from_obligation(&stack)? {
             None => None,
-            Some(candidate) => {
-                let mut candidate = self.confirm_candidate(&stack, obligation, candidate)?;
-                let inferred_obligations = (*self.inferred_obligations).into_iter().cloned();
-                candidate.nested_obligations_mut().extend(inferred_obligations);
-                Some(candidate)
-            },
+            Some(candidate) => Some(self.confirm_candidate(&stack, obligation, candidate)?),
         };
 
         // Test whether this is a `()` which was produced by defaulting a
@@ -623,7 +588,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                                 stack: TraitObligationStackList<'o, 'tcx>,
                                                 predicates: I)
                                                 -> EvaluationResult
-        where I : Iterator<Item=&'a PredicateObligation<'tcx>>, 'tcx:'a
+        where I : IntoIterator<Item=&'a PredicateObligation<'tcx>>, 'tcx:'a
     {
         let mut result = EvaluatedToOk;
         for obligation in predicates {
@@ -660,7 +625,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 // does this code ever run?
                 match self.infcx.equality_predicate(&obligation.cause, obligation.param_env, p) {
                     Ok(InferOk { obligations, .. }) => {
-                        self.inferred_obligations.extend(obligations);
+                        self.evaluate_predicates_recursively(previous_stack, &obligations);
                         EvaluatedToOk
                     },
                     Err(_) => EvaluatedToErr
@@ -671,7 +636,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 // does this code ever run?
                 match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) {
                     Some(Ok(InferOk { obligations, .. })) => {
-                        self.inferred_obligations.extend(obligations);
+                        self.evaluate_predicates_recursively(previous_stack, &obligations);
                         EvaluatedToOk
                     },
                     Some(Err(_)) => EvaluatedToErr,
@@ -685,7 +650,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                           obligation.cause.body_id,
                                           ty, obligation.cause.span) {
                     Some(obligations) =>
-                        self.evaluate_predicates_recursively(previous_stack, obligations.iter()),
+                        self.evaluate_predicates_recursively(previous_stack, &obligations),
                     None =>
                         EvaluatedToAmbig,
                 }
@@ -2474,6 +2439,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         };
 
         let mut upcast_trait_ref = None;
+        let mut nested = vec![];
         let vtable_base;
 
         {
@@ -2492,7 +2458,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         self.commit_if_ok(
                             |this, _| this.match_poly_trait_ref(obligation, t))
                     {
-                        Ok(_) => { upcast_trait_ref = Some(t); false }
+                        Ok(obligations) => {
+                            upcast_trait_ref = Some(t);
+                            nested.extend(obligations);
+                            false
+                        }
                         Err(_) => { true }
                     }
                 });
@@ -2510,7 +2480,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         VtableObjectData {
             upcast_trait_ref: upcast_trait_ref.unwrap(),
             vtable_base,
-            nested: vec![]
+            nested,
         }
     }
 
@@ -2529,17 +2499,19 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                                          util::TupleArgumentsFlag::Yes)
             .map_bound(|(trait_ref, _)| trait_ref);
 
-        let Normalized { value: trait_ref, obligations } =
+        let Normalized { value: trait_ref, mut obligations } =
             project::normalize_with_depth(self,
                                           obligation.param_env,
                                           obligation.cause.clone(),
                                           obligation.recursion_depth + 1,
                                           &trait_ref);
 
-        self.confirm_against_poly_trait_ref(obligation.cause.clone(),
-                                            obligation.param_env,
-                                            obligation.predicate,
-                                            trait_ref)?;
+        obligations.extend(
+            self.confirm_against_poly_trait_ref(obligation.cause.clone(),
+                                                obligation.param_env,
+                                                obligation.predicate,
+                                                trait_ref)?);
+
         Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations })
     }
 
@@ -2563,7 +2535,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             self.generator_trait_ref_unnormalized(obligation, closure_def_id, substs);
         let Normalized {
             value: trait_ref,
-            obligations
+            mut obligations
         } = normalize_with_depth(self,
                                  obligation.param_env,
                                  obligation.cause.clone(),
@@ -2575,10 +2547,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                trait_ref,
                obligations);
 
-        self.confirm_against_poly_trait_ref(obligation.cause.clone(),
-                                            obligation.param_env,
-                                            obligation.predicate,
-                                            trait_ref)?;
+        obligations.extend(
+            self.confirm_against_poly_trait_ref(obligation.cause.clone(),
+                                                obligation.param_env,
+                                                obligation.predicate,
+                                                trait_ref)?);
 
         Ok(VtableGeneratorData {
             closure_def_id: closure_def_id,
@@ -2621,10 +2594,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                trait_ref,
                obligations);
 
-        self.confirm_against_poly_trait_ref(obligation.cause.clone(),
-                                            obligation.param_env,
-                                            obligation.predicate,
-                                            trait_ref)?;
+        obligations.extend(
+            self.confirm_against_poly_trait_ref(obligation.cause.clone(),
+                                                obligation.param_env,
+                                                obligation.predicate,
+                                                trait_ref)?);
 
         obligations.push(Obligation::new(
             obligation.cause.clone(),
@@ -2668,13 +2642,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                       obligation_param_env: ty::ParamEnv<'tcx>,
                                       obligation_trait_ref: ty::TraitRef<'tcx>,
                                       expected_trait_ref: ty::PolyTraitRef<'tcx>)
-                                      -> Result<(), SelectionError<'tcx>>
+                                      -> Result<Vec<PredicateObligation<'tcx>>,
+                                                SelectionError<'tcx>>
     {
         let obligation_trait_ref = obligation_trait_ref.clone();
         self.infcx
             .at(&obligation_cause, obligation_param_env)
             .instantiable_as(expected_trait_ref, obligation_trait_ref)
-            .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
+            .map(|InferOk { obligations, value: () }| obligations)
             .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e))
     }
 
@@ -2708,7 +2683,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     self.infcx.at(&obligation.cause, obligation.param_env)
                               .eq(target, new_trait)
                               .map_err(|_| Unimplemented)?;
-                self.inferred_obligations.extend(obligations);
+                nested.extend(obligations);
 
                 // Register one obligation for 'a: 'b.
                 let cause = ObligationCause::new(obligation.cause.span,
@@ -2770,7 +2745,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     self.infcx.at(&obligation.cause, obligation.param_env)
                               .eq(b, a)
                               .map_err(|_| Unimplemented)?;
-                self.inferred_obligations.extend(obligations);
+                nested.extend(obligations);
             }
 
             // Struct<T> -> Struct<U>.
@@ -2834,7 +2809,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     self.infcx.at(&obligation.cause, obligation.param_env)
                               .eq(target, new_struct)
                               .map_err(|_| Unimplemented)?;
-                self.inferred_obligations.extend(obligations);
+                nested.extend(obligations);
 
                 // Construct the nested Field<T>: Unsize<Field<U>> predicate.
                 nested.push(tcx.predicate_for_trait_def(
@@ -2865,7 +2840,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     self.infcx.at(&obligation.cause, obligation.param_env)
                               .eq(target, new_tuple)
                               .map_err(|_| Unimplemented)?;
-                self.inferred_obligations.extend(obligations);
+                nested.extend(obligations);
 
                 // Construct the nested T: Unsize<U> predicate.
                 nested.push(tcx.predicate_for_trait_def(
@@ -2928,7 +2903,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs);
 
-        let impl_trait_ref =
+        let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } =
             project::normalize_with_depth(self,
                                           obligation.param_env,
                                           obligation.cause.clone(),
@@ -2942,15 +2917,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         let InferOk { obligations, .. } =
             self.infcx.at(&obligation.cause, obligation.param_env)
-                      .eq(obligation.predicate, impl_trait_ref.value)
+                      .eq(obligation.predicate, impl_trait_ref)
                       .map_err(|e| {
                           debug!("match_impl: failed eq_trait_refs due to `{}`", e);
                           ()
                       })?;
-        self.inferred_obligations.extend(obligations);
+        nested_obligations.extend(obligations);
 
         debug!("match_impl: success impl_substs={:?}", impl_substs);
-        Ok(Normalized { value: impl_substs, obligations: impl_trait_ref.obligations })
+        Ok(Normalized { value: impl_substs, obligations: nested_obligations })
     }
 
     fn fast_reject_trait_refs(&mut self,
@@ -2986,8 +2961,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                     where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
                                     -> Result<Vec<PredicateObligation<'tcx>>,()>
     {
-        self.match_poly_trait_ref(obligation, where_clause_trait_ref)?;
-        Ok(Vec::new())
+        self.match_poly_trait_ref(obligation, where_clause_trait_ref)
     }
 
     /// Returns `Ok` if `poly_trait_ref` being true implies that the
@@ -2995,7 +2969,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn match_poly_trait_ref(&mut self,
                             obligation: &TraitObligation<'tcx>,
                             poly_trait_ref: ty::PolyTraitRef<'tcx>)
-                            -> Result<(),()>
+                            -> Result<Vec<PredicateObligation<'tcx>>,()>
     {
         debug!("match_poly_trait_ref: obligation={:?} poly_trait_ref={:?}",
                obligation,
@@ -3003,7 +2977,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         self.infcx.at(&obligation.cause, obligation.param_env)
                   .instantiable_as(poly_trait_ref, obligation.predicate)
-                  .map(|InferOk { obligations, .. }| self.inferred_obligations.extend(obligations))
+                  .map(|InferOk { obligations, value: () }| obligations)
                   .map_err(|_| ())
     }
 

From 747a59f8b117c2690aa42995faee9c79d9cec053 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 10 Sep 2017 09:38:07 -0400
Subject: [PATCH 44/63] kill dead code: `Predicate::Equate`

---
 src/librustc/ich/impls_ty.rs              |  3 ---
 src/librustc/middle/free_region.rs        |  1 -
 src/librustc/traits/error_reporting.rs    | 10 ----------
 src/librustc/traits/fulfill.rs            | 13 +------------
 src/librustc/traits/object_safety.rs      |  2 --
 src/librustc/traits/select.rs             | 11 -----------
 src/librustc/traits/util.rs               |  8 --------
 src/librustc/ty/mod.rs                    | 15 ---------------
 src/librustc/ty/structural_impls.rs       |  6 ------
 src/librustc/ty/util.rs                   |  1 -
 src/librustc/ty/wf.rs                     |  4 ----
 src/librustc/util/ppaux.rs                | 11 -----------
 src/librustc_typeck/check/closure.rs      |  1 -
 src/librustc_typeck/check/method/probe.rs |  1 -
 src/librustc_typeck/check/regionck.rs     |  1 -
 src/librustdoc/clean/mod.rs               |  1 -
 16 files changed, 1 insertion(+), 88 deletions(-)

diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index a676953f12422..8b197df5d4ebd 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -214,9 +214,6 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::Predicate<'gcx> {
             ty::Predicate::Trait(ref pred) => {
                 pred.hash_stable(hcx, hasher);
             }
-            ty::Predicate::Equate(ref pred) => {
-                pred.hash_stable(hcx, hasher);
-            }
             ty::Predicate::Subtype(ref pred) => {
                 pred.hash_stable(hcx, hasher);
             }
diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs
index 3bcdc4f7e2c63..61b0c90d64c7b 100644
--- a/src/librustc/middle/free_region.rs
+++ b/src/librustc/middle/free_region.rs
@@ -142,7 +142,6 @@ impl<'tcx> FreeRegionMap<'tcx> {
             match *predicate {
                 ty::Predicate::Projection(..) |
                 ty::Predicate::Trait(..) |
-                ty::Predicate::Equate(..) |
                 ty::Predicate::Subtype(..) |
                 ty::Predicate::WellFormed(..) |
                 ty::Predicate::ObjectSafe(..) |
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 5c76ff6f895ce..603a3e67a4f61 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -619,16 +619,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                         span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate)
                     }
 
-                    ty::Predicate::Equate(ref predicate) => {
-                        let predicate = self.resolve_type_vars_if_possible(predicate);
-                        let err = self.equality_predicate(&obligation.cause,
-                                                          obligation.param_env,
-                                                          &predicate).err().unwrap();
-                        struct_span_err!(self.tcx.sess, span, E0278,
-                            "the requirement `{}` is not satisfied (`{}`)",
-                            predicate, err)
-                    }
-
                     ty::Predicate::RegionOutlives(ref predicate) => {
                         let predicate = self.resolve_type_vars_if_possible(predicate);
                         let err = self.region_outlives_predicate(&obligation.cause,
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index 7fe67577858ac..f734c609d4d43 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use infer::{InferCtxt, InferOk};
+use infer::InferCtxt;
 use ty::{self, Ty, TypeFoldable, ToPredicate};
 use ty::error::ExpectedFound;
 use rustc_data_structures::obligation_forest::{ObligationForest, Error};
@@ -414,17 +414,6 @@ fn process_predicate<'a, 'gcx, 'tcx>(
             }
         }
 
-        ty::Predicate::Equate(ref binder) => {
-            match selcx.infcx().equality_predicate(&obligation.cause,
-                                                   obligation.param_env,
-                                                   binder) {
-                Ok(InferOk { obligations, value: () }) => {
-                    Ok(Some(obligations))
-                },
-                Err(_) => Err(CodeSelectionError(Unimplemented)),
-            }
-        }
-
         ty::Predicate::RegionOutlives(ref binder) => {
             match selcx.infcx().region_outlives_predicate(&obligation.cause,
                                                           obligation.param_env,
diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs
index 1e9816095ea2e..4dffb58d631bc 100644
--- a/src/librustc/traits/object_safety.rs
+++ b/src/librustc/traits/object_safety.rs
@@ -169,7 +169,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                     ty::Predicate::RegionOutlives(..) |
                     ty::Predicate::ClosureKind(..) |
                     ty::Predicate::Subtype(..) |
-                    ty::Predicate::Equate(..) |
                     ty::Predicate::ConstEvaluatable(..) => {
                         false
                     }
@@ -198,7 +197,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                     }
                     ty::Predicate::Projection(..) |
                     ty::Predicate::Trait(..) |
-                    ty::Predicate::Equate(..) |
                     ty::Predicate::Subtype(..) |
                     ty::Predicate::RegionOutlives(..) |
                     ty::Predicate::WellFormed(..) |
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index f3a80529505c4..fd426d7d51258 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -621,17 +621,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 self.evaluate_trait_predicate_recursively(previous_stack, obligation)
             }
 
-            ty::Predicate::Equate(ref p) => {
-                // does this code ever run?
-                match self.infcx.equality_predicate(&obligation.cause, obligation.param_env, p) {
-                    Ok(InferOk { obligations, .. }) => {
-                        self.evaluate_predicates_recursively(previous_stack, &obligations);
-                        EvaluatedToOk
-                    },
-                    Err(_) => EvaluatedToErr
-                }
-            }
-
             ty::Predicate::Subtype(ref p) => {
                 // does this code ever run?
                 match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) {
diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs
index 49e11c4f28f55..f5fe133ef4d56 100644
--- a/src/librustc/traits/util.rs
+++ b/src/librustc/traits/util.rs
@@ -25,9 +25,6 @@ fn anonymize_predicate<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
         ty::Predicate::Trait(ref data) =>
             ty::Predicate::Trait(tcx.anonymize_late_bound_regions(data)),
 
-        ty::Predicate::Equate(ref data) =>
-            ty::Predicate::Equate(tcx.anonymize_late_bound_regions(data)),
-
         ty::Predicate::RegionOutlives(ref data) =>
             ty::Predicate::RegionOutlives(tcx.anonymize_late_bound_regions(data)),
 
@@ -163,11 +160,6 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
                 // Currently, we do not elaborate object-safe
                 // predicates.
             }
-            ty::Predicate::Equate(..) => {
-                // Currently, we do not "elaborate" predicates like
-                // `X == Y`, though conceivably we might. For example,
-                // `&X == &Y` implies that `X == Y`.
-            }
             ty::Predicate::Subtype(..) => {
                 // Currently, we do not "elaborate" predicates like `X
                 // <: Y`, though conceivably we might.
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index c013aaa424891..a9ac3ee804c5d 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -868,9 +868,6 @@ pub enum Predicate<'tcx> {
     /// would be the type parameters.
     Trait(PolyTraitRef<'tcx>),
 
-    /// where `T1 == T2`.
-    Equate(PolyEquatePredicate<'tcx>),
-
     /// where 'a : 'b
     RegionOutlives(PolyRegionOutlivesPredicate<'tcx>),
 
@@ -973,8 +970,6 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> {
         match *self {
             Predicate::Trait(ty::Binder(ref data)) =>
                 Predicate::Trait(ty::Binder(data.subst(tcx, substs))),
-            Predicate::Equate(ty::Binder(ref data)) =>
-                Predicate::Equate(ty::Binder(data.subst(tcx, substs))),
             Predicate::Subtype(ty::Binder(ref data)) =>
                 Predicate::Subtype(ty::Binder(data.subst(tcx, substs))),
             Predicate::RegionOutlives(ty::Binder(ref data)) =>
@@ -1076,12 +1071,6 @@ impl<'tcx> ToPredicate<'tcx> for PolyTraitRef<'tcx> {
     }
 }
 
-impl<'tcx> ToPredicate<'tcx> for PolyEquatePredicate<'tcx> {
-    fn to_predicate(&self) -> Predicate<'tcx> {
-        Predicate::Equate(self.clone())
-    }
-}
-
 impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
     fn to_predicate(&self) -> Predicate<'tcx> {
         Predicate::RegionOutlives(self.clone())
@@ -1109,9 +1098,6 @@ impl<'tcx> Predicate<'tcx> {
             ty::Predicate::Trait(ref data) => {
                 data.skip_binder().input_types().collect()
             }
-            ty::Predicate::Equate(ty::Binder(ref data)) => {
-                vec![data.0, data.1]
-            }
             ty::Predicate::Subtype(ty::Binder(SubtypePredicate { a, b, a_is_expected: _ })) => {
                 vec![a, b]
             }
@@ -1152,7 +1138,6 @@ impl<'tcx> Predicate<'tcx> {
                 Some(t)
             }
             Predicate::Projection(..) |
-            Predicate::Equate(..) |
             Predicate::Subtype(..) |
             Predicate::RegionOutlives(..) |
             Predicate::WellFormed(..) |
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index af3f313cbd2d1..a522cf785ab31 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -196,9 +196,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> {
             ty::Predicate::Trait(ref binder) => {
                 tcx.lift(binder).map(ty::Predicate::Trait)
             }
-            ty::Predicate::Equate(ref binder) => {
-                tcx.lift(binder).map(ty::Predicate::Equate)
-            }
             ty::Predicate::Subtype(ref binder) => {
                 tcx.lift(binder).map(ty::Predicate::Subtype)
             }
@@ -976,8 +973,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
         match *self {
             ty::Predicate::Trait(ref a) =>
                 ty::Predicate::Trait(a.fold_with(folder)),
-            ty::Predicate::Equate(ref binder) =>
-                ty::Predicate::Equate(binder.fold_with(folder)),
             ty::Predicate::Subtype(ref binder) =>
                 ty::Predicate::Subtype(binder.fold_with(folder)),
             ty::Predicate::RegionOutlives(ref binder) =>
@@ -1000,7 +995,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
     fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
         match *self {
             ty::Predicate::Trait(ref a) => a.visit_with(visitor),
-            ty::Predicate::Equate(ref binder) => binder.visit_with(visitor),
             ty::Predicate::Subtype(ref binder) => binder.visit_with(visitor),
             ty::Predicate::RegionOutlives(ref binder) => binder.visit_with(visitor),
             ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor),
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 2a7b0fb20e9fa..c46192982c3fd 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -385,7 +385,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                 match predicate {
                     ty::Predicate::Projection(..) |
                     ty::Predicate::Trait(..) |
-                    ty::Predicate::Equate(..) |
                     ty::Predicate::Subtype(..) |
                     ty::Predicate::WellFormed(..) |
                     ty::Predicate::ObjectSafe(..) |
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs
index eeed684b5698c..df715d589f083 100644
--- a/src/librustc/ty/wf.rs
+++ b/src/librustc/ty/wf.rs
@@ -77,10 +77,6 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
         ty::Predicate::Trait(ref t) => {
             wf.compute_trait_ref(&t.skip_binder(), Elaborate::None); // (*)
         }
-        ty::Predicate::Equate(ref t) => {
-            wf.compute(t.skip_binder().0);
-            wf.compute(t.skip_binder().1);
-        }
         ty::Predicate::RegionOutlives(..) => {
         }
         ty::Predicate::TypeOutlives(ref t) => {
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index c2cdc19fa2ee2..81b593d0a0a4f 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -912,7 +912,6 @@ define_print_multi! {
     ('tcx) ty::Binder<ty::TraitRefPrintWithColon<'tcx>>,
     ('tcx) ty::Binder<ty::TraitRefPrintWithoutSelf<'tcx>>,
     ('tcx) ty::Binder<ty::FnSig<'tcx>>,
-    ('tcx) ty::Binder<ty::EquatePredicate<'tcx>>,
     ('tcx) ty::Binder<ty::SubtypePredicate<'tcx>>,
     ('tcx) ty::Binder<ty::ProjectionPredicate<'tcx>>,
     ('tcx) ty::Binder<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>,
@@ -1204,14 +1203,6 @@ define_print! {
     }
 }
 
-define_print! {
-    ('tcx) ty::EquatePredicate<'tcx>, (self, f, cx) {
-        display {
-            print!(f, cx, print(self.0), write(" == "), print(self.1))
-        }
-    }
-}
-
 define_print! {
     ('tcx) ty::SubtypePredicate<'tcx>, (self, f, cx) {
         display {
@@ -1267,7 +1258,6 @@ define_print! {
         display {
             match *self {
                 ty::Predicate::Trait(ref data) => data.print_with_colon().print(f, cx),
-                ty::Predicate::Equate(ref predicate) => predicate.print(f, cx),
                 ty::Predicate::Subtype(ref predicate) => predicate.print(f, cx),
                 ty::Predicate::RegionOutlives(ref predicate) => predicate.print(f, cx),
                 ty::Predicate::TypeOutlives(ref predicate) => predicate.print(f, cx),
@@ -1292,7 +1282,6 @@ define_print! {
         debug {
             match *self {
                 ty::Predicate::Trait(ref a) => a.print_with_colon().print(f, cx),
-                ty::Predicate::Equate(ref pair) => pair.print(f, cx),
                 ty::Predicate::Subtype(ref pair) => pair.print(f, cx),
                 ty::Predicate::RegionOutlives(ref pair) => pair.print(f, cx),
                 ty::Predicate::TypeOutlives(ref pair) => pair.print(f, cx),
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index ee0e29c1bb6e3..8d12976255cec 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -221,7 +221,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 let opt_trait_ref = match obligation.predicate {
                     ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref(self.tcx)),
                     ty::Predicate::Trait(data) => Some(data),
-                    ty::Predicate::Equate(..) => None,
                     ty::Predicate::Subtype(..) => None,
                     ty::Predicate::RegionOutlives(..) => None,
                     ty::Predicate::TypeOutlives(..) => None,
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 59aa427a12c3d..2be213c0b065f 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -598,7 +598,6 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                             _ => None,
                         }
                     }
-                    ty::Predicate::Equate(..) |
                     ty::Predicate::Subtype(..) |
                     ty::Predicate::Projection(..) |
                     ty::Predicate::RegionOutlives(..) |
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 155173b641700..77dfc790338ab 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -507,7 +507,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
                         assert!(!obligation.has_escaping_regions());
                         match obligation.predicate {
                             ty::Predicate::Trait(..) |
-                            ty::Predicate::Equate(..) |
                             ty::Predicate::Subtype(..) |
                             ty::Predicate::Projection(..) |
                             ty::Predicate::ClosureKind(..) |
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 59c5ae92bbc8c..88f15e401f439 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -965,7 +965,6 @@ impl<'a> Clean<WherePredicate> for ty::Predicate<'a> {
 
         match *self {
             Predicate::Trait(ref pred) => pred.clean(cx),
-            Predicate::Equate(ref pred) => pred.clean(cx),
             Predicate::Subtype(ref pred) => pred.clean(cx),
             Predicate::RegionOutlives(ref pred) => pred.clean(cx),
             Predicate::TypeOutlives(ref pred) => pred.clean(cx),

From 61c223c5a114fd46f43c5c04adac8957beed49eb Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 14 Sep 2017 04:10:09 -0400
Subject: [PATCH 45/63] tweak `ToPredicate` trait

---
 src/librustc/ty/mod.rs | 26 ++++++++++++++++----------
 src/librustc/ty/wf.rs  |  2 +-
 2 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index a9ac3ee804c5d..32af7191d6576 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1056,36 +1056,42 @@ impl<'tcx> ToPolyTraitRef<'tcx> for TraitRef<'tcx> {
 }
 
 pub trait ToPredicate<'tcx> {
-    fn to_predicate(&self) -> Predicate<'tcx>;
+    fn to_predicate(self) -> Predicate<'tcx>;
+}
+
+impl<'tcx> ToPredicate<'tcx> for Predicate<'tcx> {
+    fn to_predicate(self) -> Predicate<'tcx> {
+        self
+    }
 }
 
 impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> {
-    fn to_predicate(&self) -> Predicate<'tcx> {
+    fn to_predicate(self) -> Predicate<'tcx> {
         ty::Predicate::Trait(self.to_poly_trait_ref())
     }
 }
 
 impl<'tcx> ToPredicate<'tcx> for PolyTraitRef<'tcx> {
-    fn to_predicate(&self) -> Predicate<'tcx> {
-        ty::Predicate::Trait(*self)
+    fn to_predicate(self) -> Predicate<'tcx> {
+        ty::Predicate::Trait(self)
     }
 }
 
 impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
-    fn to_predicate(&self) -> Predicate<'tcx> {
-        Predicate::RegionOutlives(self.clone())
+    fn to_predicate(self) -> Predicate<'tcx> {
+        Predicate::RegionOutlives(self)
     }
 }
 
 impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
-    fn to_predicate(&self) -> Predicate<'tcx> {
-        Predicate::TypeOutlives(self.clone())
+    fn to_predicate(self) -> Predicate<'tcx> {
+        Predicate::TypeOutlives(self)
     }
 }
 
 impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
-    fn to_predicate(&self) -> Predicate<'tcx> {
-        Predicate::Projection(self.clone())
+    fn to_predicate(self) -> Predicate<'tcx> {
+        Predicate::Projection(self)
     }
 }
 
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs
index df715d589f083..2d0cb106075cc 100644
--- a/src/librustc/ty/wf.rs
+++ b/src/librustc/ty/wf.rs
@@ -208,7 +208,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
         if !data.has_escaping_regions() {
             let predicate = trait_ref.to_predicate();
             let cause = self.cause(traits::ProjectionWf(data));
-            self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
+            self.out.push(traits::Obligation::predicate(cause, self.param_env, predicate));
         }
     }
 

From 52d3c36ded407418ab69428532701a22e3519e60 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 14 Sep 2017 04:19:17 -0400
Subject: [PATCH 46/63] kill `ToPolyTraitRef` trait

Instead, just roll the functionality into `to_predicate`
---
 src/librustc/traits/project.rs              |  6 +++---
 src/librustc/traits/select.rs               |  6 ++----
 src/librustc/ty/mod.rs                      | 14 ++------------
 src/librustc/ty/wf.rs                       |  3 +--
 src/librustc_typeck/check/method/mod.rs     |  5 ++---
 src/librustc_typeck/check/method/suggest.rs |  5 ++---
 src/librustc_typeck/collect.rs              |  4 ++--
 7 files changed, 14 insertions(+), 29 deletions(-)

diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 263ecc77d5398..a463d3727c54d 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -31,7 +31,7 @@ use middle::const_val::ConstVal;
 use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
 use syntax::symbol::Symbol;
 use ty::subst::{Subst, Substs};
-use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
+use ty::{self, ToPredicate, Ty, TyCtxt};
 use ty::fold::{TypeFoldable, TypeFolder};
 use util::common::FN_OUTPUT_NAME;
 
@@ -670,7 +670,7 @@ fn push_paranoid_cache_value_obligation<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a,
                                                         depth: usize,
                                                         result: &mut NormalizedTy<'tcx>)
 {
-    let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
+    let trait_ref = projection_ty.trait_ref(infcx.tcx);
     let trait_obligation = Obligation { cause,
                                         recursion_depth: depth,
                                         param_env,
@@ -704,7 +704,7 @@ fn normalize_to_error<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tc
                                       depth: usize)
                                       -> NormalizedTy<'tcx>
 {
-    let trait_ref = projection_ty.trait_ref(selcx.tcx()).to_poly_trait_ref();
+    let trait_ref = projection_ty.trait_ref(selcx.tcx());
     let trait_obligation = Obligation { cause,
                                         recursion_depth: depth,
                                         param_env,
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index fd426d7d51258..a31fec8ef6ea4 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -35,7 +35,7 @@ use hir::def_id::DefId;
 use infer;
 use infer::{InferCtxt, InferOk, TypeFreshener};
 use ty::subst::{Kind, Subst, Substs};
-use ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use ty::fast_reject;
 use ty::relate::TypeRelation;
 use middle::lang_items;
@@ -843,9 +843,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             debug!("evaluate_stack({:?}) --> recursive",
                    stack.fresh_trait_ref);
             let cycle = stack.iter().skip(1).take(rec_index+1);
-            let cycle = cycle.map(|stack| {
-                ty::Predicate::Trait(stack.obligation.predicate.to_poly_trait_ref())
-            });
+            let cycle = cycle.map(|stack| stack.obligation.predicate.to_predicate());
             if self.coinductive_match(cycle) {
                 debug!("evaluate_stack({:?}) --> recursive, coinductive",
                        stack.fresh_trait_ref);
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 32af7191d6576..2b2231258b617 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1044,17 +1044,6 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
     }
 }
 
-pub trait ToPolyTraitRef<'tcx> {
-    fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
-}
-
-impl<'tcx> ToPolyTraitRef<'tcx> for TraitRef<'tcx> {
-    fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> {
-        assert!(!self.has_escaping_regions());
-        ty::Binder(self.clone())
-    }
-}
-
 pub trait ToPredicate<'tcx> {
     fn to_predicate(self) -> Predicate<'tcx>;
 }
@@ -1067,7 +1056,8 @@ impl<'tcx> ToPredicate<'tcx> for Predicate<'tcx> {
 
 impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> {
     fn to_predicate(self) -> Predicate<'tcx> {
-        ty::Predicate::Trait(self.to_poly_trait_ref())
+        assert!(!self.has_escaping_regions());
+        ty::Predicate::Trait(ty::Binder(self))
     }
 }
 
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs
index 2d0cb106075cc..24e953bcd8ce0 100644
--- a/src/librustc/ty/wf.rs
+++ b/src/librustc/ty/wf.rs
@@ -206,9 +206,8 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
         self.compute_trait_ref(&trait_ref, Elaborate::None);
 
         if !data.has_escaping_regions() {
-            let predicate = trait_ref.to_predicate();
             let cause = self.cause(traits::ProjectionWf(data));
-            self.out.push(traits::Obligation::predicate(cause, self.param_env, predicate));
+            self.out.push(traits::Obligation::new(cause, self.param_env, trait_ref.to_predicate()));
         }
     }
 
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 9a0aec595604d..7a49c7b8694d0 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -16,7 +16,7 @@ use hir::def_id::DefId;
 use namespace::Namespace;
 use rustc::ty::subst::Substs;
 use rustc::traits;
-use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
+use rustc::ty::{self, Ty, ToPredicate, TraitRef, TypeFoldable};
 use rustc::ty::subst::Subst;
 use rustc::infer::{self, InferOk};
 
@@ -264,12 +264,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let trait_ref = ty::TraitRef::new(trait_def_id, substs);
 
         // Construct an obligation
-        let poly_trait_ref = trait_ref.to_poly_trait_ref();
         let obligation =
             traits::Obligation::misc(span,
                                      self.body_id,
                                      self.param_env,
-                                     poly_trait_ref.to_predicate());
+                                     trait_ref.to_predicate());
 
         // Now we want to know if this can be matched
         let mut selcx = traits::SelectionContext::new(self);
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 91100c626cf83..ae2f9673ab027 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -13,7 +13,7 @@
 
 use check::FnCtxt;
 use rustc::hir::map as hir_map;
-use rustc::ty::{self, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TypeFoldable};
+use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
 use hir::def::Def;
 use hir::def_id::{CRATE_DEF_INDEX, DefId};
 use middle::lang_items::FnOnceTraitLangItem;
@@ -57,12 +57,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             &[self.next_ty_var(ty::UniverseIndex::ROOT,
                                                TypeVariableOrigin::MiscVariable(span))]);
                         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,
                                              self.param_env,
-                                             poly_trait_ref.to_predicate());
+                                             trait_ref.to_predicate());
                         SelectionContext::new(self).evaluate_obligation(&obligation)
                     })
                 })
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 814470974285f..c57c7c0b537bb 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -33,7 +33,7 @@ use middle::resolve_lifetime as rl;
 use rustc::traits::Reveal;
 use rustc::ty::subst::Substs;
 use rustc::ty::{ToPredicate, ReprOptions};
-use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt};
+use rustc::ty::{self, AdtKind, Ty, TyCtxt};
 use rustc::ty::maps::Providers;
 use rustc::ty::util::IntTypeExt;
 use util::nodemap::FxHashMap;
@@ -1405,7 +1405,7 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         // Add in a predicate that `Self:Trait` (where `Trait` is the
         // current trait).  This is needed for builtin bounds.
-        predicates.push(trait_ref.to_poly_trait_ref().to_predicate());
+        predicates.push(trait_ref.to_predicate());
     }
 
     // Collect the region predicates that were declared inline as

From 7bd2820a555ac04f552154c7cbe331388dc88efc Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Fri, 15 Sep 2017 05:33:06 -0400
Subject: [PATCH 47/63] rework how closures deduce their signature

Create a special list of pending closure obligations rather than using
the main list of pending predicates.
---
 src/librustc/traits/fulfill.rs       | 90 +++++++++++++++++++++++++++-
 src/librustc_typeck/check/closure.rs | 23 +++----
 2 files changed, 96 insertions(+), 17 deletions(-)

diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index f734c609d4d43..2efc4bbd2fe67 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -49,6 +49,42 @@ pub struct FulfillmentContext<'tcx> {
     // fulfillment context.
     predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
 
+    // A set of predicates that are useful for inferring closure signatures.
+    //
+    // We are looking to help out with a scenario like this:
+    //
+    // ```
+    // foo(|x| use(*x))
+    //
+    // fn foo<T>(t: T) where T: FnMut(&u32) { .. }
+    // ```
+    //
+    // In this case, in the expression `foo(|x| use(*x))`, the type
+    // variable `T` is first instantiated with an inference variable
+    // `?T` (as part of the subexpression `foo`) and the obligation
+    // that `?T: FnMut(&u32)` is registered; actually, a few
+    // obligations get registered, but the most helpful thing is a
+    // projection predicate `for<'a> <?T as FnMut<(&'a u32,)>>::Output
+    // = ()`. We then typecheck the arguments. The closure expression
+    // `|x|` gets that its expected type is `?T`, but that does not
+    // tell it anything about its expected signature.  To solve this,
+    // it needs to trawl through the list of pending prediates. That's
+    // where this vector comes in.
+    //
+    // This vector stores a copy of predicate that gets registered where the
+    // following conditions are met:
+    //
+    // 0. The predicate is in universe 0.
+    // 1. The self-type is an uninstantiated type variable.
+    // 2. It is a projection or trait-ref of a closure trait.
+    //
+    // We periodically prune out cases where the self-type got
+    // instantiated, as they are no longer of potential use.
+    //
+    // Closures can read the current state via the
+    // `pending_closure_predicates()` accessor.
+    pending_closure_predicates: Vec<ty::Predicate<'tcx>>,
+
     // A set of constraints that regionck must validate. Each
     // constraint has the form `T:'a`, meaning "some type `T` must
     // outlive the lifetime 'a". These constraints derive from
@@ -95,6 +131,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         FulfillmentContext {
             predicates: ObligationForest::new(),
             region_obligations: NodeMap(),
+            pending_closure_predicates: vec![],
         }
     }
 
@@ -173,6 +210,8 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         // debug output much nicer to read and so on.
         let obligation = infcx.resolve_type_vars_if_possible(&obligation);
 
+        self.record_closure_predicate_if_relevant(infcx, &obligation);
+
         debug!("register_predicate_obligation(obligation={:?})", obligation);
 
         assert!(!infcx.is_in_snapshot());
@@ -183,6 +222,46 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         });
     }
 
+    fn record_closure_predicate_if_relevant(&mut self,
+                                            infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                            obligation: &PredicateObligation<'tcx>) {
+        if obligation.param_env.universe != ty::UniverseIndex::ROOT {
+            return;
+        }
+
+        if Self::is_closure_predicate(infcx, &obligation.predicate) {
+            self.prune_pending_closure_predicates(infcx);
+            self.pending_closure_predicates.push(obligation.predicate);
+        }
+    }
+
+    fn is_closure_predicate(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                            predicate: &ty::Predicate<'tcx>)
+                            -> bool
+    {
+        let opt_trait_ref = match *predicate {
+            ty::Predicate::Trait(trait_ref) => trait_ref,
+            ty::Predicate::Projection(proj) => proj.to_poly_trait_ref(infcx.tcx),
+            _ => return false,
+        };
+
+        // OK to skip binder. We are looking for a (free) type
+        // variable.
+        let self_ty = opt_trait_ref.skip_binder().self_ty();
+        if !self_ty.is_ty_var() {
+            return false;
+        }
+
+        // Now that we know `self_ty` is a type variable, check that
+        // it is still unresolved.
+        let self_ty = infcx.shallow_resolve(self_ty);
+        self_ty.is_ty_var()
+    }
+
+    pub fn prune_pending_closure_predicates(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) {
+        self.pending_closure_predicates.retain(|p| Self::is_closure_predicate(infcx, p))
+    }
+
     pub fn register_predicate_obligations(&mut self,
                                           infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                           obligations: Vec<PredicateObligation<'tcx>>)
@@ -229,8 +308,15 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         self.select(&mut selcx)
     }
 
-    pub fn pending_obligations(&self) -> Vec<PendingPredicateObligation<'tcx>> {
-        self.predicates.pending_obligations()
+    /// Returns the list of closure obligations that we have seen
+    /// which are yet pending (meaning that their self type has not
+    /// yet been seen to be resolved to a specific closure type, and
+    /// hence they may still be of use).
+    ///
+    /// See the comment on the field `pending_closure_predicates` for
+    /// more information.
+    pub fn pending_closure_predicates(&self) -> &[ty::Predicate<'tcx>] {
+        &self.pending_closure_predicates
     }
 
     /// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index 8d12976255cec..8b47ad6639ca4 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -186,17 +186,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let fulfillment_cx = self.fulfillment_cx.borrow();
         // Here `expected_ty` is known to be a type inference variable.
 
-        let expected_sig = fulfillment_cx
-            .pending_obligations()
+        let expected_sig = fulfillment_cx.pending_closure_predicates()
             .iter()
-            .map(|obligation| &obligation.obligation)
-            .filter_map(|obligation| {
-                debug!(
-                    "deduce_expectations_from_obligations: obligation.predicate={:?}",
-                    obligation.predicate
-                );
-
-                match obligation.predicate {
+            .filter_map(|predicate| {
+                debug!("deduce_expectations_from_obligations: predicate={:?}", predicate);
+
+                match *predicate {
                     // Given a Projection predicate, we can potentially infer
                     // the complete signature.
                     ty::Predicate::Projection(ref proj_predicate) => {
@@ -213,12 +208,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // infer the kind. This can occur if there is a trait-reference
         // like `F : Fn<A>`. Note that due to subtyping we could encounter
         // many viable options, so pick the most restrictive.
-        let expected_kind = fulfillment_cx
-            .pending_obligations()
+        let expected_kind = fulfillment_cx.pending_closure_predicates()
             .iter()
-            .map(|obligation| &obligation.obligation)
-            .filter_map(|obligation| {
-                let opt_trait_ref = match obligation.predicate {
+            .filter_map(|predicate| {
+                let opt_trait_ref = match *predicate {
                     ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref(self.tcx)),
                     ty::Predicate::Trait(data) => Some(data),
                     ty::Predicate::Subtype(..) => None,

From 708754179157c868e572cf84d467904368df9640 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 14 Sep 2017 09:09:17 -0400
Subject: [PATCH 48/63] Split `Predicate` and `PredicateAtom`.

The binders now move into the `Predicate::Poly` variant, rather than
being distributed throughout the variants.
---
 src/librustc/ich/impls_ty.rs                  |  35 +-
 src/librustc/infer/at.rs                      |  78 +++--
 src/librustc/infer/combine.rs                 |   9 +-
 src/librustc/infer/error_reporting/mod.rs     |  12 +-
 src/librustc/infer/higher_ranked/mod.rs       |  20 ++
 src/librustc/infer/mod.rs                     |  55 ++--
 src/librustc/infer/sub.rs                     |  15 +-
 src/librustc/middle/free_region.rs            |  24 +-
 src/librustc/traits/error_reporting.rs        |  93 +++---
 src/librustc/traits/fulfill.rs                | 199 +++++------
 src/librustc/traits/mod.rs                    |  24 +-
 src/librustc/traits/object_safety.rs          |  57 ++--
 src/librustc/traits/project.rs                | 247 +++++++-------
 src/librustc/traits/select.rs                 |  79 +++--
 src/librustc/traits/specialize/mod.rs         |   2 +-
 src/librustc/traits/util.rs                   | 251 ++++++--------
 src/librustc/ty/fold.rs                       |  13 +-
 src/librustc/ty/mod.rs                        | 308 +++++++++++-------
 src/librustc/ty/structural_impls.rs           | 118 ++++---
 src/librustc/ty/sty.rs                        |  16 +-
 src/librustc/ty/util.rs                       |  24 +-
 src/librustc/ty/wf.rs                         |  77 +++--
 src/librustc/util/ppaux.rs                    |  54 +--
 src/librustc_privacy/lib.rs                   |  39 +--
 src/librustc_typeck/astconv.rs                |  12 +-
 src/librustc_typeck/check/closure.rs          |  85 +++--
 src/librustc_typeck/check/coercion.rs         |  24 +-
 src/librustc_typeck/check/method/confirm.rs   |  11 +-
 src/librustc_typeck/check/method/mod.rs       |  10 +-
 src/librustc_typeck/check/method/probe.rs     |  27 +-
 src/librustc_typeck/check/mod.rs              |  17 +-
 src/librustc_typeck/check/regionck.rs         |  51 +--
 src/librustc_typeck/check/wfcheck.rs          |  13 +-
 src/librustc_typeck/collect.rs                |   8 +-
 .../constrained_type_params.rs                |   8 +-
 src/librustdoc/clean/simplify.rs              |   3 +-
 36 files changed, 1121 insertions(+), 997 deletions(-)

diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 8b197df5d4ebd..20a45387f115b 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -211,32 +211,49 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::Predicate<'gcx> {
                                           hasher: &mut StableHasher<W>) {
         mem::discriminant(self).hash_stable(hcx, hasher);
         match *self {
-            ty::Predicate::Trait(ref pred) => {
+            ty::Predicate::Poly(ref a) => {
+                a.hash_stable(hcx, hasher);
+            }
+
+            ty::Predicate::Atom(ref a) => {
+                a.hash_stable(hcx, hasher);
+            }
+        }
+    }
+}
+
+impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'gcx>> for ty::PredicateAtom<'gcx> {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'gcx>,
+                                          hasher: &mut StableHasher<W>) {
+        mem::discriminant(self).hash_stable(hcx, hasher);
+        match *self {
+            ty::PredicateAtom::Trait(ref pred) => {
                 pred.hash_stable(hcx, hasher);
             }
-            ty::Predicate::Subtype(ref pred) => {
+            ty::PredicateAtom::Subtype(ref pred) => {
                 pred.hash_stable(hcx, hasher);
             }
-            ty::Predicate::RegionOutlives(ref pred) => {
+            ty::PredicateAtom::RegionOutlives(ref pred) => {
                 pred.hash_stable(hcx, hasher);
             }
-            ty::Predicate::TypeOutlives(ref pred) => {
+            ty::PredicateAtom::TypeOutlives(ref pred) => {
                 pred.hash_stable(hcx, hasher);
             }
-            ty::Predicate::Projection(ref pred) => {
+            ty::PredicateAtom::Projection(ref pred) => {
                 pred.hash_stable(hcx, hasher);
             }
-            ty::Predicate::WellFormed(ty) => {
+            ty::PredicateAtom::WellFormed(ty) => {
                 ty.hash_stable(hcx, hasher);
             }
-            ty::Predicate::ObjectSafe(def_id) => {
+            ty::PredicateAtom::ObjectSafe(def_id) => {
                 def_id.hash_stable(hcx, hasher);
             }
-            ty::Predicate::ClosureKind(def_id, closure_kind) => {
+            ty::PredicateAtom::ClosureKind(def_id, closure_kind) => {
                 def_id.hash_stable(hcx, hasher);
                 closure_kind.hash_stable(hcx, hasher);
             }
-            ty::Predicate::ConstEvaluatable(def_id, substs) => {
+            ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
                 def_id.hash_stable(hcx, hasher);
                 substs.hash_stable(hcx, hasher);
             }
diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs
index b29a390485c26..42c6085e7d7fb 100644
--- a/src/librustc/infer/at.rs
+++ b/src/librustc/infer/at.rs
@@ -62,11 +62,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 }
 
 pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
-    fn to_trace(cause: &ObligationCause<'tcx>,
-                a_is_expected: bool,
-                a: Self,
-                b: Self)
-                -> TypeTrace<'tcx>;
+    fn to_trace<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                          cause: &ObligationCause<'tcx>,
+                          a_is_expected: bool,
+                          a: Self,
+                          b: Self)
+                          -> TypeTrace<'tcx>;
 }
 
 impl<'a, 'gcx, 'tcx> At<'a, 'gcx, 'tcx> {
@@ -141,6 +142,17 @@ impl<'a, 'gcx, 'tcx> At<'a, 'gcx, 'tcx> {
         self.eq(expected_prime, actual)
     }
 
+    /// Like `instantiable_as`, but just returns true if no immediate
+    /// error would result.
+    pub fn can_instantiate_as<T>(self,
+                                 expected: ty::Binder<T>,
+                                 actual: T)
+                                 -> bool
+        where T: ToTrace<'tcx>
+    {
+        self.infcx.probe(|_| self.instantiable_as(expected, actual).is_ok())
+    }
+
     /// Make `expected <: actual`
     pub fn sub<T>(self,
                   expected: T,
@@ -221,7 +233,7 @@ impl<'a, 'gcx, 'tcx> At<'a, 'gcx, 'tcx> {
                         -> Trace<'a, 'gcx, 'tcx>
         where T: ToTrace<'tcx>
     {
-        let trace = ToTrace::to_trace(self.cause, a_is_expected, a, b);
+        let trace = ToTrace::to_trace(self.infcx.tcx, self.cause, a_is_expected, a, b);
         Trace { at: self, trace: trace, a_is_expected }
     }
 }
@@ -298,43 +310,61 @@ impl<'a, 'gcx, 'tcx> Trace<'a, 'gcx, 'tcx> {
 }
 
 impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
-    fn to_trace(cause: &ObligationCause<'tcx>,
-                a_is_expected: bool,
-                a: Self,
-                b: Self)
-                -> TypeTrace<'tcx>
+    fn to_trace<'a, 'gcx>(_: TyCtxt<'a, 'gcx, 'tcx>,
+                          cause: &ObligationCause<'tcx>,
+                          a_is_expected: bool,
+                          a: Self,
+                          b: Self)
+                          -> TypeTrace<'tcx>
+    {
+        TypeTrace {
+            cause: cause.clone(),
+            values: ValuePairs::Types(ExpectedFound::new(a_is_expected, a, b))
+        }
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for ty::ProjectionTy<'tcx> {
+    fn to_trace<'a, 'gcx>(_: TyCtxt<'a, 'gcx, 'tcx>,
+                          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))
+            values: ValuePairs::ProjectionTypes(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>
+    fn to_trace<'a, 'gcx>(_: TyCtxt<'a, 'gcx, 'tcx>,
+                          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))
+            values: ValuePairs::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>
+    fn to_trace<'a, 'gcx>(_: TyCtxt<'a, 'gcx, 'tcx>,
+                          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))
+            values: ValuePairs::PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b))
         }
     }
 }
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 412ca286188c3..48b0ccc747411 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -45,7 +45,7 @@ use ty::{self, Ty, TyCtxt};
 use ty::error::TypeError;
 use ty::relate::{self, Relate, RelateResult, TypeRelation};
 use ty::subst::Substs;
-use traits::{Obligation, PredicateObligations};
+use traits::{PredicateObligation, PredicateObligations};
 
 use syntax::ast;
 use syntax_pos::Span;
@@ -227,9 +227,10 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
         self.infcx.type_variables.borrow_mut().instantiate(b_vid, b_ty);
 
         if needs_wf {
-            self.obligations.push(Obligation::new(self.trace.cause.clone(),
-                                                  param_env,
-                                                  ty::Predicate::WellFormed(b_ty)));
+            self.obligations.push(
+                PredicateObligation::from(self.trace.cause.clone(),
+                                          param_env,
+                                          ty::PredicateAtom::WellFormed(b_ty)));
         }
 
         // Finally, relate `b_ty` to `a_ty`, as described in previous comment.
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 486c4dcc99709..72b3f8837a98e 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -55,8 +55,7 @@
 //! ported to this system, and which relies on string concatenation at the
 //! time of error detection.
 
-use infer;
-use super::{InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs};
+use infer::{self, InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs};
 use super::region_inference::{RegionResolutionError, ConcreteFailure, SubSupConflict,
                               GenericBoundFailure, GenericKind};
 
@@ -797,13 +796,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         -> Option<(DiagnosticStyledString, DiagnosticStyledString)>
     {
         match *values {
-            infer::Types(ref exp_found) => {
+            ValuePairs::Types(ref exp_found) => {
                 self.expected_found_str_ty(exp_found)
             }
-            infer::TraitRefs(ref exp_found) => {
+            ValuePairs::ProjectionTypes(ref exp_found) => {
+                self.expected_found_str(exp_found)
+            }
+            ValuePairs::TraitRefs(ref exp_found) => {
                 self.expected_found_str(&exp_found.map(|t| t.print_with_colon()))
             }
-            infer::PolyTraitRefs(ref exp_found) => {
+            ValuePairs::PolyTraitRefs(ref exp_found) => {
                 self.expected_found_str(&exp_found.map(|t| t.print_with_colon()))
             }
         }
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index 30e15133b0f09..59b13fb25acce 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -18,6 +18,7 @@ use super::{CombinedSnapshot,
 use super::combine::CombineFields;
 use super::region_inference::taint::TaintIterator;
 
+use traits;
 use ty::{self, TyCtxt, Binder, TypeFoldable};
 use ty::relate::{Relate, RelateResult, TypeRelation};
 use std::usize;
@@ -477,4 +478,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         (result, param_env, map)
     }
+
+    /// Given a predicate obligation, which may include binders,
+    /// convert into the underlying predicate atom obligation, which
+    /// may be in another universe (if binders were present).
+    pub fn skolemize_predicate_obligation(&self, obligation: &traits::PredicateObligation<'tcx>)
+                                          -> traits::PredicateAtomObligation<'tcx>
+    {
+        match obligation.predicate {
+            ty::Predicate::Poly(ref binder) => {
+                let (atom, param_env, _skol_map) =
+                    self.skolemize_late_bound_regions(obligation.param_env, binder);
+                obligation.with(atom).with_env(param_env)
+            }
+
+            ty::Predicate::Atom(atom) => {
+                obligation.with(atom)
+            }
+        }
+    }
 }
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index abe8a35b32928..c46fa0b955a2b 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -13,7 +13,6 @@
 pub use self::LateBoundRegionConversionTime::*;
 pub use self::RegionVariableOrigin::*;
 pub use self::SubregionOrigin::*;
-pub use self::ValuePairs::*;
 pub use ty::IntVarValue;
 pub use self::freshen::TypeFreshener;
 pub use self::region_inference::{GenericKind, VerifyBound};
@@ -109,7 +108,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     // the set of predicates on which errors have been reported, to
     // avoid reporting the same error twice.
-    pub reported_trait_errors: RefCell<FxHashMap<Span, Vec<ty::Predicate<'tcx>>>>,
+    pub reported_trait_errors: RefCell<FxHashMap<Span, Vec<ty::PredicateAtom<'tcx>>>>,
 
     // When an error occurs, we want to avoid reporting "derived"
     // errors that are due to this original failure. Normally, we
@@ -144,6 +143,7 @@ pub type SkolemizationMap<'tcx> = FxHashMap<ty::BoundRegion, ty::Region<'tcx>>;
 #[derive(Clone, Debug)]
 pub enum ValuePairs<'tcx> {
     Types(ExpectedFound<Ty<'tcx>>),
+    ProjectionTypes(ExpectedFound<ty::ProjectionTy<'tcx>>),
     TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>),
     PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>),
 }
@@ -878,24 +878,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.region_vars.make_subregion(origin, param_env, a, b);
     }
 
-    pub fn equality_predicate(&self,
-                              cause: &ObligationCause<'tcx>,
-                              param_env: ty::ParamEnv<'tcx>,
-                              predicate: &ty::PolyEquatePredicate<'tcx>)
-        -> InferResult<'tcx, ()>
-    {
-        self.commit_if_ok(|_snapshot| {
-            let (ty::EquatePredicate(a, b), param_env, _skol_map) =
-                self.skolemize_late_bound_regions(param_env, predicate);
-            let eqty_ok = self.at(cause, param_env).eq(b, a)?;
-            Ok(eqty_ok.unit())
-        })
-    }
-
     pub fn subtype_predicate(&self,
                              cause: &ObligationCause<'tcx>,
                              param_env: ty::ParamEnv<'tcx>,
-                             predicate: &ty::PolySubtypePredicate<'tcx>)
+                             predicate: ty::SubtypePredicate<'tcx>)
         -> Option<InferResult<'tcx, ()>>
     {
         // Subtle: it's ok to skip the binder here and resolve because
@@ -908,8 +894,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         // micro-optimization. Naturally I could not
         // resist. -nmatsakis
         let two_unbound_type_vars = {
-            let a = self.shallow_resolve(predicate.skip_binder().a);
-            let b = self.shallow_resolve(predicate.skip_binder().b);
+            let a = self.shallow_resolve(predicate.a);
+            let b = self.shallow_resolve(predicate.b);
             a.is_ty_var() && b.is_ty_var()
         };
 
@@ -919,9 +905,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
 
         Some(self.commit_if_ok(|_snapshot| {
-            let (ty::SubtypePredicate { a_is_expected, a, b}, param_env, _skol_map) =
-                self.skolemize_late_bound_regions(param_env, predicate);
-
+            let ty::SubtypePredicate { a_is_expected, a, b} = predicate;
             let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?;
             Ok(ok.unit())
         }))
@@ -930,11 +914,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     pub fn region_outlives_predicate(&self,
                                      cause: &traits::ObligationCause<'tcx>,
                                      param_env: ty::ParamEnv<'tcx>,
-                                     predicate: &ty::PolyRegionOutlivesPredicate<'tcx>)
+                                     predicate: ty::RegionOutlivesPredicate<'tcx>)
                                      -> UnitResult<'tcx>
     {
-        let (ty::OutlivesPredicate(r_a, r_b), _param_env, _skol_map) =
-            self.skolemize_late_bound_regions(param_env, predicate);
+        let ty::OutlivesPredicate(r_a, r_b) = predicate;
         let origin =
             SubregionOrigin::from_obligation_cause(cause,
                                                    || RelateRegionParamBound(cause.span));
@@ -1338,14 +1321,14 @@ impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> {
                  -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: Types(ExpectedFound::new(a_is_expected, a, b))
+            values: ValuePairs::Types(ExpectedFound::new(a_is_expected, a, b))
         }
     }
 
     pub fn dummy(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: ObligationCause::dummy(),
-            values: Types(ExpectedFound {
+            values: ValuePairs::Types(ExpectedFound {
                 expected: tcx.types.err,
                 found: tcx.types.err,
             })
@@ -1435,21 +1418,21 @@ impl RegionVariableOrigin {
 impl<'tcx> TypeFoldable<'tcx> for ValuePairs<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         match *self {
-            ValuePairs::Types(ref ef) => {
-                ValuePairs::Types(ef.fold_with(folder))
-            }
-            ValuePairs::TraitRefs(ref ef) => {
-                ValuePairs::TraitRefs(ef.fold_with(folder))
-            }
-            ValuePairs::PolyTraitRefs(ref ef) => {
-                ValuePairs::PolyTraitRefs(ef.fold_with(folder))
-            }
+            ValuePairs::Types(ref ef) =>
+                ValuePairs::Types(ef.fold_with(folder)),
+            ValuePairs::ProjectionTypes(ref ef) =>
+                ValuePairs::ProjectionTypes(ef.fold_with(folder)),
+            ValuePairs::TraitRefs(ref ef) =>
+                ValuePairs::TraitRefs(ef.fold_with(folder)),
+            ValuePairs::PolyTraitRefs(ref ef) =>
+                ValuePairs::PolyTraitRefs(ef.fold_with(folder)),
         }
     }
 
     fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
         match *self {
             ValuePairs::Types(ref ef) => ef.visit_with(visitor),
+            ValuePairs::ProjectionTypes(ref ef) => ef.visit_with(visitor),
             ValuePairs::TraitRefs(ref ef) => ef.visit_with(visitor),
             ValuePairs::PolyTraitRefs(ref ef) => ef.visit_with(visitor),
         }
diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs
index 2ac386ffec68f..ac192f0ce0f1f 100644
--- a/src/librustc/infer/sub.rs
+++ b/src/librustc/infer/sub.rs
@@ -11,7 +11,7 @@
 use super::SubregionOrigin;
 use super::combine::{CombineFields, RelationDir};
 
-use traits::Obligation;
+use traits::PredicateObligation;
 use ty::{self, Ty, TyCtxt};
 use ty::TyVar;
 use ty::fold::TypeFoldable;
@@ -97,15 +97,14 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
                 // is important to the occurs check later on.
                 infcx.type_variables.borrow_mut().sub(a_vid, b_vid);
                 self.fields.obligations.push(
-                    Obligation::new(
+                    PredicateObligation::from(
                         self.fields.trace.cause.clone(),
                         self.param_env,
-                        ty::Predicate::Subtype(
-                            ty::Binder(ty::SubtypePredicate {
-                                a_is_expected: self.a_is_expected,
-                                a,
-                                b,
-                            }))));
+                        ty::SubtypePredicate {
+                            a_is_expected: self.a_is_expected,
+                            a,
+                            b,
+                        }));
 
                 Ok(a)
             }
diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs
index 61b0c90d64c7b..492dfecbb5d04 100644
--- a/src/librustc/middle/free_region.rs
+++ b/src/librustc/middle/free_region.rs
@@ -139,18 +139,22 @@ impl<'tcx> FreeRegionMap<'tcx> {
                                                predicates: &[ty::Predicate<'tcx>]) {
         debug!("relate_free_regions_from_predicates(predicates={:?})", predicates);
         for predicate in predicates {
-            match *predicate {
-                ty::Predicate::Projection(..) |
-                ty::Predicate::Trait(..) |
-                ty::Predicate::Subtype(..) |
-                ty::Predicate::WellFormed(..) |
-                ty::Predicate::ObjectSafe(..) |
-                ty::Predicate::ClosureKind(..) |
-                ty::Predicate::TypeOutlives(..) |
-                ty::Predicate::ConstEvaluatable(..) => {
+            // Ok to skip binders because we are only looking for free
+            // regions, which are not late bound.
+            let predicate_atom = predicate.skip_binders();
+
+            match predicate_atom {
+                ty::PredicateAtom::Projection(..) |
+                ty::PredicateAtom::Trait(..) |
+                ty::PredicateAtom::Subtype(..) |
+                ty::PredicateAtom::WellFormed(..) |
+                ty::PredicateAtom::ObjectSafe(..) |
+                ty::PredicateAtom::ClosureKind(..) |
+                ty::PredicateAtom::TypeOutlives(..) |
+                ty::PredicateAtom::ConstEvaluatable(..) => {
                     // No region bounds here
                 }
-                ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => {
+                ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
                     self.relate_regions(r_b, r_a);
                 }
             }
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 603a3e67a4f61..74962d2213976 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -20,7 +20,7 @@ use super::{
     OutputTypeParameterMismatch,
     TraitNotObjectSafe,
     ConstEvalFailure,
-    PredicateObligation,
+    PredicateAtomObligation,
     Reveal,
     SelectionContext,
     SelectionError,
@@ -52,14 +52,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                      body_id: Option<hir::BodyId>) {
         #[derive(Debug)]
         struct ErrorDescriptor<'tcx> {
-            predicate: ty::Predicate<'tcx>,
+            predicate: ty::PredicateAtom<'tcx>,
             index: Option<usize>, // None if this is an old error
         }
 
         let mut error_map : FxHashMap<_, _> =
             self.reported_trait_errors.borrow().iter().map(|(&span, predicates)| {
-                (span, predicates.iter().map(|predicate| ErrorDescriptor {
-                    predicate: predicate.clone(),
+                (span, predicates.iter().map(|&predicate| ErrorDescriptor {
+                    predicate: predicate,
                     index: None
                 }).collect())
             }).collect();
@@ -117,8 +117,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     // returns if `cond` not occurring implies that `error` does not occur - i.e. that
     // `error` occurring implies that `cond` occurs.
     fn error_implies(&self,
-                     cond: &ty::Predicate<'tcx>,
-                     error: &ty::Predicate<'tcx>)
+                     cond: &ty::PredicateAtom<'tcx>,
+                     error: &ty::PredicateAtom<'tcx>)
                      -> bool
     {
         if cond == error {
@@ -126,7 +126,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
 
         let (cond, error) = match (cond, error) {
-            (&ty::Predicate::Trait(..), &ty::Predicate::Trait(error))
+            (&ty::PredicateAtom::Trait(..), &ty::PredicateAtom::Trait(error))
                 => (cond, error),
             _ => {
                 // FIXME: make this work in other cases too.
@@ -134,14 +134,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             }
         };
 
-        for implication in super::elaborate_predicates(self.tcx, vec![cond.clone()]) {
-            if let ty::Predicate::Trait(implication) = implication {
+        for implication in super::elaborate_predicates(self.tcx, vec![cond.to_predicate()]) {
+            if let Some(implication) = implication.poly_trait(self.tcx) {
                 // FIXME: I'm just not taking associated types at all here.
                 // Eventually I'll need to implement param-env-aware
                 // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic.
+                let cause = ObligationCause::dummy();
                 let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
-                if let Ok(_) = self.can_sub(param_env, error, implication) {
-                    debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication);
+                if self.at(&cause, param_env).can_instantiate_as(implication, error) {
+                    debug!("error_implies: {:?} => {:?} => {:?}", cond, implication, error);
                     return true
                 }
             }
@@ -150,7 +151,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         false
     }
 
-    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>,
+    fn report_fulfillment_error(&self,
+                                error: &FulfillmentError<'tcx>,
                                 body_id: Option<hir::BodyId>) {
         debug!("report_fulfillment_errors({:?})", error);
         match error.code {
@@ -174,7 +176,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 
     fn report_projection_error(&self,
-                               obligation: &PredicateObligation<'tcx>,
+                               obligation: &PredicateAtomObligation<'tcx>,
                                error: &MismatchedProjectionTypes<'tcx>)
     {
         let predicate =
@@ -193,13 +195,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             //
             // this can fail if the problem was higher-ranked, in which
             // cause I have no idea for a good error message.
-            if let ty::Predicate::Projection(ref data) = predicate {
+            if let ty::PredicateAtom::Projection(data) = predicate {
                 let mut selcx = SelectionContext::new(self);
-                let (data, _) = self.replace_late_bound_regions_with_fresh_var(
-                    obligation.cause.span,
-                    obligation.param_env.universe,
-                    infer::LateBoundRegionConversionTime::HigherRankedType,
-                    data);
                 let normalized = super::normalize_projection_type(
                     &mut selcx,
                     obligation.param_env,
@@ -270,13 +267,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 
     fn impl_similar_to(&self,
-                       trait_ref: ty::PolyTraitRef<'tcx>,
-                       obligation: &PredicateObligation<'tcx>)
+                       trait_ref: ty::TraitRef<'tcx>,
+                       obligation: &PredicateAtomObligation<'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();
 
         let mut self_match_impls = vec![];
@@ -323,13 +319,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
     fn on_unimplemented_note(
         &self,
-        trait_ref: ty::PolyTraitRef<'tcx>,
-        obligation: &PredicateObligation<'tcx>) ->
+        trait_ref: ty::TraitRef<'tcx>,
+        obligation: &PredicateAtomObligation<'tcx>) ->
         OnUnimplementedNote
     {
-        let def_id = self.impl_similar_to(trait_ref, obligation)
-            .unwrap_or(trait_ref.def_id());
-        let trait_ref = *trait_ref.skip_binder();
+        let def_id = self.impl_similar_to(trait_ref, obligation).unwrap_or(trait_ref.def_id);
 
         let desugaring;
         let method;
@@ -374,16 +368,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 
     fn find_similar_impl_candidates(&self,
-                                    trait_ref: ty::PolyTraitRef<'tcx>)
+                                    trait_ref: ty::TraitRef<'tcx>)
                                     -> Vec<ty::TraitRef<'tcx>>
     {
         let simp = fast_reject::simplify_type(self.tcx,
-                                              trait_ref.skip_binder().self_ty(),
+                                              trait_ref.self_ty(),
                                               true);
         let mut impl_candidates = Vec::new();
 
         match simp {
-            Some(simp) => self.tcx.for_each_impl(trait_ref.def_id(), |def_id| {
+            Some(simp) => self.tcx.for_each_impl(trait_ref.def_id, |def_id| {
                 let imp = self.tcx.impl_trait_ref(def_id).unwrap();
                 let imp_simp = fast_reject::simplify_type(self.tcx,
                                                           imp.self_ty(),
@@ -395,7 +389,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 }
                 impl_candidates.push(imp);
             }),
-            None => self.tcx.for_each_impl(trait_ref.def_id(), |def_id| {
+            None => self.tcx.for_each_impl(trait_ref.def_id, |def_id| {
                 impl_candidates.push(
                     self.tcx.impl_trait_ref(def_id).unwrap());
             })
@@ -461,7 +455,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// that we can give a more helpful error message (and, in particular,
     /// we do not suggest increasing the overflow limit, which is not
     /// going to help).
-    pub fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
+    pub fn report_overflow_error_cycle(&self, cycle: &[PredicateAtomObligation<'tcx>]) -> ! {
         let cycle = self.resolve_type_vars_if_possible(&cycle.to_owned());
         assert!(cycle.len() > 0);
 
@@ -529,7 +523,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 
     pub fn report_selection_error(&self,
-                                  obligation: &PredicateObligation<'tcx>,
+                                  obligation: &PredicateAtomObligation<'tcx>,
                                   error: &SelectionError<'tcx>)
     {
         let span = obligation.cause.span;
@@ -550,7 +544,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     return;
                 }
                 match obligation.predicate {
-                    ty::Predicate::Trait(ref trait_ref) => {
+                    ty::PredicateAtom::Trait(ref trait_ref) => {
                         let trait_ref = self.resolve_type_vars_if_possible(trait_ref);
 
                         if self.tcx.sess.has_errors() && trait_ref.references_error() {
@@ -612,24 +606,24 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                         err
                     }
 
-                    ty::Predicate::Subtype(ref predicate) => {
+                    ty::PredicateAtom::Subtype(ref predicate) => {
                         // Errors for Subtype predicates show up as
                         // `FulfillmentErrorCode::CodeSubtypeError`,
                         // not selection error.
                         span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate)
                     }
 
-                    ty::Predicate::RegionOutlives(ref predicate) => {
+                    ty::PredicateAtom::RegionOutlives(ref predicate) => {
                         let predicate = self.resolve_type_vars_if_possible(predicate);
                         let err = self.region_outlives_predicate(&obligation.cause,
                                                                  obligation.param_env,
-                                                                 &predicate).err().unwrap();
+                                                                 predicate).err().unwrap();
                         struct_span_err!(self.tcx.sess, span, E0279,
                             "the requirement `{}` is not satisfied (`{}`)",
                             predicate, err)
                     }
 
-                    ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => {
+                    ty::PredicateAtom::Projection(..) | ty::PredicateAtom::TypeOutlives(..) => {
                         let predicate =
                             self.resolve_type_vars_if_possible(&obligation.predicate);
                         struct_span_err!(self.tcx.sess, span, E0280,
@@ -637,14 +631,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                             predicate)
                     }
 
-                    ty::Predicate::ObjectSafe(trait_def_id) => {
+                    ty::PredicateAtom::ObjectSafe(trait_def_id) => {
                         let violations = self.tcx.object_safety_violations(trait_def_id);
                         self.tcx.report_object_safety_error(span,
                                                             trait_def_id,
                                                             violations)
                     }
 
-                    ty::Predicate::ClosureKind(closure_def_id, kind) => {
+                    ty::PredicateAtom::ClosureKind(closure_def_id, kind) => {
                         let found_kind = self.closure_kind(closure_def_id).unwrap();
                         let closure_span = self.tcx.hir.span_if_local(closure_def_id).unwrap();
                         let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
@@ -683,7 +677,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                         return;
                     }
 
-                    ty::Predicate::WellFormed(ty) => {
+                    ty::PredicateAtom::WellFormed(ty) => {
                         // WF predicates cannot themselves make
                         // errors. They can only block due to
                         // ambiguity; otherwise, they always
@@ -692,7 +686,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                         span_bug!(span, "WF predicate not satisfied for {:?}", ty);
                     }
 
-                    ty::Predicate::ConstEvaluatable(..) => {
+                    ty::PredicateAtom::ConstEvaluatable(..) => {
                         // Errors for `ConstEvaluatable` predicates show up as
                         // `SelectionError::ConstEvalFailure`,
                         // not `Unimplemented`.
@@ -1012,7 +1006,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 }
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
-    fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>,
+    fn maybe_report_ambiguity(&self,
+                              obligation: &PredicateAtomObligation<'tcx>,
                               body_id: Option<hir::BodyId>) {
         // Unable to successfully determine, probably means
         // insufficient type information, but could mean
@@ -1033,7 +1028,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
 
         match predicate {
-            ty::Predicate::Trait(trait_ref) => {
+            ty::PredicateAtom::Trait(trait_ref) => {
                 let self_ty = trait_ref.self_ty();
                 if predicate.references_error() {
                     return;
@@ -1065,7 +1060,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 if !self.tcx.sess.has_errors() {
                     if
                         self.tcx.lang_items().sized_trait()
-                        .map_or(false, |sized_id| sized_id == trait_ref.def_id())
+                        .map_or(false, |sized_id| sized_id == trait_ref.def_id)
                     {
                         self.need_type_info(body_id, span, self_ty);
                     } else {
@@ -1080,7 +1075,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 }
             }
 
-            ty::Predicate::WellFormed(ty) => {
+            ty::PredicateAtom::WellFormed(ty) => {
                 // Same hacky approach as above to avoid deluging user
                 // with error messages.
                 if !ty.references_error() && !self.tcx.sess.has_errors() {
@@ -1088,11 +1083,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 }
             }
 
-            ty::Predicate::Subtype(ref data) => {
+            ty::PredicateAtom::Subtype(ref data) => {
                 if data.references_error() || self.tcx.sess.has_errors() {
                     // no need to overload user in such cases
                 } else {
-                    let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder();
+                    let &SubtypePredicate { a_is_expected: _, a, b } = data;
                     // both must be type variables, or the other would've been instantiated
                     assert!(a.is_ty_var() && b.is_ty_var());
                     self.need_type_info(body_id,
@@ -1119,7 +1114,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// to the type parameters.
     fn predicate_can_apply(&self,
                            param_env: ty::ParamEnv<'tcx>,
-                           pred: ty::PolyTraitRef<'tcx>)
+                           pred: ty::TraitRef<'tcx>)
                            -> bool {
         struct ParamToVarFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
             infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index 2efc4bbd2fe67..a5a3754af3902 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -22,13 +22,13 @@ use super::CodeAmbiguity;
 use super::CodeProjectionError;
 use super::CodeSelectionError;
 use super::{FulfillmentError, FulfillmentErrorCode};
-use super::{ObligationCause, PredicateObligation, Obligation};
+use super::{ObligationCause, PredicateAtomObligation, PredicateObligation, Obligation};
 use super::project;
 use super::select::SelectionContext;
 use super::{Unimplemented, ConstEvalFailure};
 
-impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
-    type Predicate = ty::Predicate<'tcx>;
+impl<'tcx> ForestObligation for PendingPredicateAtomObligation<'tcx> {
+    type Predicate = ty::PredicateAtom<'tcx>;
 
     fn as_predicate(&self) -> &Self::Predicate { &self.obligation.predicate }
 }
@@ -47,7 +47,7 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
 pub struct FulfillmentContext<'tcx> {
     // A list of all obligations that have been registered with this
     // fulfillment context.
-    predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
+    predicates: ObligationForest<PendingPredicateAtomObligation<'tcx>>,
 
     // A set of predicates that are useful for inferring closure signatures.
     //
@@ -114,14 +114,15 @@ pub struct FulfillmentContext<'tcx> {
 
 #[derive(Clone)]
 pub struct RegionObligation<'tcx> {
+    pub param_env: ty::ParamEnv<'tcx>,
     pub sub_region: ty::Region<'tcx>,
     pub sup_type: Ty<'tcx>,
     pub cause: ObligationCause<'tcx>,
 }
 
 #[derive(Clone, Debug)]
-pub struct PendingPredicateObligation<'tcx> {
-    pub obligation: PredicateObligation<'tcx>,
+pub struct PendingPredicateAtomObligation<'tcx> {
+    pub obligation: PredicateAtomObligation<'tcx>,
     pub stalled_on: Vec<Ty<'tcx>>,
 }
 
@@ -195,28 +196,39 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
     }
 
     pub fn register_region_obligation(&mut self,
+                                      param_env: ty::ParamEnv<'tcx>,
                                       t_a: Ty<'tcx>,
                                       r_b: ty::Region<'tcx>,
                                       cause: ObligationCause<'tcx>)
     {
-        register_region_obligation(t_a, r_b, cause, &mut self.region_obligations);
+        register_region_obligation(param_env, t_a, r_b, cause, &mut self.region_obligations);
     }
 
     pub fn register_predicate_obligation(&mut self,
                                          infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                          obligation: PredicateObligation<'tcx>)
+    {
+        // Important: record closure predicates *before* we skolemize!
+        // Otherwise it is too late to recover binding information.
+        self.record_closure_predicate_if_relevant(infcx, &obligation);
+
+        let atom_obligation = infcx.skolemize_predicate_obligation(&obligation);
+        self.register_predicate_atom_obligation(infcx, atom_obligation);
+    }
+
+    fn register_predicate_atom_obligation(&mut self,
+                                          infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                          obligation: PredicateAtomObligation<'tcx>)
     {
         // this helps to reduce duplicate errors, as well as making
         // debug output much nicer to read and so on.
         let obligation = infcx.resolve_type_vars_if_possible(&obligation);
 
-        self.record_closure_predicate_if_relevant(infcx, &obligation);
-
-        debug!("register_predicate_obligation(obligation={:?})", obligation);
+        debug!("register_predicate_atom_obligation(obligation={:?})", obligation);
 
         assert!(!infcx.is_in_snapshot());
 
-        self.predicates.register_obligation(PendingPredicateObligation {
+        self.predicates.register_obligation(PendingPredicateAtomObligation {
             obligation,
             stalled_on: vec![]
         });
@@ -230,7 +242,9 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         }
 
         if Self::is_closure_predicate(infcx, &obligation.predicate) {
-            self.prune_pending_closure_predicates(infcx);
+            // Don't let the vector keep growing without bound: clear
+            // out anything that is no longer relevant.
+            self.pending_closure_predicates.retain(|p| Self::is_closure_predicate(infcx, p));
             self.pending_closure_predicates.push(obligation.predicate);
         }
     }
@@ -239,29 +253,25 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
                             predicate: &ty::Predicate<'tcx>)
                             -> bool
     {
-        let opt_trait_ref = match *predicate {
-            ty::Predicate::Trait(trait_ref) => trait_ref,
-            ty::Predicate::Projection(proj) => proj.to_poly_trait_ref(infcx.tcx),
+        // OK to skip binder. We are just looking for a (free) type
+        // variable.
+        let self_ty = match predicate.skip_binders() {
+            ty::PredicateAtom::Trait(trait_ref) => trait_ref.self_ty(),
+            ty::PredicateAtom::Projection(proj) => proj.to_trait_ref(infcx.tcx).self_ty(),
             _ => return false,
         };
 
-        // OK to skip binder. We are looking for a (free) type
-        // variable.
-        let self_ty = opt_trait_ref.skip_binder().self_ty();
+        // Not a type variable? Then not a closure predicate.
         if !self_ty.is_ty_var() {
             return false;
         }
 
-        // Now that we know `self_ty` is a type variable, check that
-        // it is still unresolved.
+        // If it is a type variable, check if it has been resolved
+        // already.  If so, no longer of interest.
         let self_ty = infcx.shallow_resolve(self_ty);
         self_ty.is_ty_var()
     }
 
-    pub fn prune_pending_closure_predicates(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) {
-        self.pending_closure_predicates.retain(|p| Self::is_closure_predicate(infcx, p))
-    }
-
     pub fn register_predicate_obligations(&mut self,
                                           infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                           obligations: Vec<PredicateObligation<'tcx>>)
@@ -367,25 +377,41 @@ struct FulfillProcessor<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> {
 }
 
 impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, 'tcx> {
-    type Obligation = PendingPredicateObligation<'tcx>;
+    type Obligation = PendingPredicateAtomObligation<'tcx>;
     type Error = FulfillmentErrorCode<'tcx>;
 
     fn process_obligation(&mut self,
                           obligation: &mut Self::Obligation)
                           -> Result<Option<Vec<Self::Obligation>>, Self::Error>
     {
-        process_predicate(self.selcx,
-                          obligation,
-                          self.region_obligations)
-            .map(|os| os.map(|os| os.into_iter().map(|o| PendingPredicateObligation {
-                obligation: o,
-                stalled_on: vec![]
-            }).collect()))
+        let opt_obligations = process_predicate(self.selcx, obligation, self.region_obligations)?;
+
+        // Return Ok(None) if we failed to make any progress.
+        let obligations = match opt_obligations {
+            None => return Ok(None),
+            Some(vec) => vec,
+        };
+
+        // To start, we have to fully skolemize all the
+        // subobligations, removing any higher-ranked bindings.
+        let atom_obligations = obligations.iter().map(|obligation| {
+            self.selcx.infcx().skolemize_predicate_obligation(obligation)
+        });
+
+        // Next, convert them into "pending obligations" that are not yet stalled on anything.
+        let pending_obligations = atom_obligations.map(|atom_obligation| {
+            PendingPredicateAtomObligation {
+                obligation: atom_obligation,
+                stalled_on: vec![],
+            }
+        });
+
+        Ok(Some(pending_obligations.collect()))
     }
 
     fn process_backedge<'c, I>(&mut self, cycle: I,
-                               _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>)
-        where I: Clone + Iterator<Item=&'c PendingPredicateObligation<'tcx>>,
+                               _marker: PhantomData<&'c PendingPredicateAtomObligation<'tcx>>)
+        where I: Clone + Iterator<Item=&'c PendingPredicateAtomObligation<'tcx>>,
     {
         if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
             debug!("process_child_obligations: coinductive match");
@@ -398,10 +424,9 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
 
 /// Return the set of type variables contained in a trait ref
 fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tcx>,
-                                       t: ty::PolyTraitRef<'tcx>) -> Vec<Ty<'tcx>>
+                                       t: ty::TraitRef<'tcx>) -> Vec<Ty<'tcx>>
 {
-    t.skip_binder() // ok b/c this check doesn't care about regions
-     .input_types()
+    t.input_types()
      .map(|t| selcx.infcx().resolve_type_vars_if_possible(&t))
      .filter(|t| t.has_infer_types())
      .flat_map(|t| t.walk())
@@ -415,7 +440,7 @@ fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 't
 /// - `Err` if the predicate does not hold
 fn process_predicate<'a, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'a, 'gcx, 'tcx>,
-    pending_obligation: &mut PendingPredicateObligation<'tcx>,
+    pending_obligation: &mut PendingPredicateAtomObligation<'tcx>,
     region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>)
     -> Result<Option<Vec<PredicateObligation<'tcx>>>,
               FulfillmentErrorCode<'tcx>>
@@ -443,8 +468,8 @@ fn process_predicate<'a, 'gcx, 'tcx>(
     }
 
     match obligation.predicate {
-        ty::Predicate::Trait(trait_ref) => {
-            let trait_obligation = obligation.with(trait_ref.clone());
+        ty::PredicateAtom::Trait(trait_ref) => {
+            let trait_obligation = obligation.with(trait_ref);
 
             if trait_ref.is_global() {
                 // no type variables present, can use evaluation for better caching.
@@ -452,15 +477,16 @@ fn process_predicate<'a, 'gcx, 'tcx>(
                 if
                     // make defaulted unit go through the slow path for better warnings,
                     // please remove this when the warnings are removed.
-                    !trait_obligation.predicate.skip_binder().self_ty().is_defaulted_unit() &&
-                    selcx.evaluate_obligation_conservatively(&obligation) {
+                    !trait_obligation.predicate.self_ty().is_defaulted_unit() &&
+                    selcx.evaluate_obligation_conservatively(&obligation)
+                {
                     debug!("selecting trait `{:?}` at depth {} evaluated to holds",
                            trait_ref, obligation.recursion_depth);
                     return Ok(Some(vec![]))
                 }
             }
 
-            match selcx.select_poly(&trait_obligation) {
+            match selcx.select(&trait_obligation) {
                 Ok(Some(vtable)) => {
                     debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
                            trait_ref, obligation.recursion_depth);
@@ -500,58 +526,31 @@ fn process_predicate<'a, 'gcx, 'tcx>(
             }
         }
 
-        ty::Predicate::RegionOutlives(ref binder) => {
+        ty::PredicateAtom::RegionOutlives(data) => {
             match selcx.infcx().region_outlives_predicate(&obligation.cause,
                                                           obligation.param_env,
-                                                          binder) {
+                                                          data) {
                 Ok(()) => Ok(Some(Vec::new())),
                 Err(_) => Err(CodeSelectionError(Unimplemented)),
             }
         }
 
-        ty::Predicate::TypeOutlives(ref binder) => {
-            // Check if there are higher-ranked regions.
-            match selcx.tcx().no_late_bound_regions(binder) {
-                // If there are, inspect the underlying type further.
-                None => {
-                    // Convert from `Binder<OutlivesPredicate<Ty, Region>>` to `Binder<Ty>`.
-                    let binder = binder.map_bound_ref(|pred| pred.0);
-
-                    // Check if the type has any bound regions.
-                    match selcx.tcx().no_late_bound_regions(&binder) {
-                        // If so, this obligation is an error (for now). Eventually we should be
-                        // able to support additional cases here, like `for<'a> &'a str: 'a`.
-                        None => {
-                            Err(CodeSelectionError(Unimplemented))
-                        }
-                        // Otherwise, we have something of the form
-                        // `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`.
-                        Some(t_a) => {
-                            let r_static = selcx.tcx().types.re_static;
-                            register_region_obligation(t_a, r_static,
-                                                       obligation.cause.clone(),
-                                                       region_obligations);
-                            Ok(Some(vec![]))
-                        }
-                    }
-                }
-                // If there aren't, register the obligation.
-                Some(ty::OutlivesPredicate(t_a, r_b)) => {
-                    register_region_obligation(t_a, r_b,
-                                               obligation.cause.clone(),
-                                               region_obligations);
-                    Ok(Some(vec![]))
-                }
-            }
+        ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => {
+            register_region_obligation(obligation.param_env,
+                                       t_a,
+                                       r_b,
+                                       obligation.cause.clone(),
+                                       region_obligations);
+            Ok(Some(vec![]))
         }
 
-        ty::Predicate::Projection(ref data) => {
-            let project_obligation = obligation.with(data.clone());
-            match project::poly_project_and_unify_type(selcx, &project_obligation) {
+        ty::PredicateAtom::Projection(data) => {
+            let project_obligation = obligation.with(data);
+            match project::project_and_unify_type(selcx, &project_obligation) {
                 Ok(None) => {
                     let tcx = selcx.tcx();
                     pending_obligation.stalled_on =
-                        trait_ref_type_vars(selcx, data.to_poly_trait_ref(tcx));
+                        trait_ref_type_vars(selcx, data.to_trait_ref(tcx));
                     Ok(None)
                 }
                 Ok(v) => Ok(v),
@@ -559,7 +558,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
             }
         }
 
-        ty::Predicate::ObjectSafe(trait_def_id) => {
+        ty::PredicateAtom::ObjectSafe(trait_def_id) => {
             if !selcx.tcx().is_object_safe(trait_def_id) {
                 Err(CodeSelectionError(Unimplemented))
             } else {
@@ -567,7 +566,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
             }
         }
 
-        ty::Predicate::ClosureKind(closure_def_id, kind) => {
+        ty::PredicateAtom::ClosureKind(closure_def_id, kind) => {
             match selcx.infcx().closure_kind(closure_def_id) {
                 Some(closure_kind) => {
                     if closure_kind.extends(kind) {
@@ -582,11 +581,12 @@ fn process_predicate<'a, 'gcx, 'tcx>(
             }
         }
 
-        ty::Predicate::WellFormed(ty) => {
+        ty::PredicateAtom::WellFormed(ty) => {
             match ty::wf::obligations(selcx.infcx(),
                                       obligation.param_env,
                                       obligation.cause.body_id,
-                                      ty, obligation.cause.span) {
+                                      ty,
+                                      obligation.cause.span) {
                 None => {
                     pending_obligation.stalled_on = vec![ty];
                     Ok(None)
@@ -595,29 +595,28 @@ fn process_predicate<'a, 'gcx, 'tcx>(
             }
         }
 
-        ty::Predicate::Subtype(ref subtype) => {
+        ty::PredicateAtom::Subtype(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,
-                                                         subtype.skip_binder().b];
+                    pending_obligation.stalled_on = vec![subtype.a, subtype.b];
                     Ok(None)
                 }
                 Some(Ok(ok)) => {
                     Ok(Some(ok.obligations))
                 }
                 Some(Err(err)) => {
-                    let expected_found = ExpectedFound::new(subtype.skip_binder().a_is_expected,
-                                                            subtype.skip_binder().a,
-                                                            subtype.skip_binder().b);
+                    let expected_found = ExpectedFound::new(subtype.a_is_expected,
+                                                            subtype.a,
+                                                            subtype.b);
                     Err(FulfillmentErrorCode::CodeSubtypeError(expected_found, err))
                 }
             }
         }
 
-        ty::Predicate::ConstEvaluatable(def_id, substs) => {
+        ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
             match selcx.tcx().lift_to_global(&obligation.param_env) {
                 None => {
                     Ok(None)
@@ -643,14 +642,16 @@ fn process_predicate<'a, 'gcx, 'tcx>(
 }
 
 
-fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
+fn register_region_obligation<'tcx>(param_env: ty::ParamEnv<'tcx>,
+                                    t_a: Ty<'tcx>,
                                     r_b: ty::Region<'tcx>,
                                     cause: ObligationCause<'tcx>,
                                     region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>)
 {
-    let region_obligation = RegionObligation { sup_type: t_a,
-                                               sub_region: r_b,
-                                               cause: cause };
+    let region_obligation = RegionObligation { param_env,
+                                               cause,
+                                               sup_type: t_a,
+                                               sub_region: r_b };
 
     debug!("register_region_obligation({:?}, cause={:?})",
            region_obligation, region_obligation.cause);
@@ -662,7 +663,7 @@ fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
 }
 
 fn to_fulfillment_error<'tcx>(
-    error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>)
+    error: Error<PendingPredicateAtomObligation<'tcx>, FulfillmentErrorCode<'tcx>>)
     -> FulfillmentError<'tcx>
 {
     let obligation = error.backtrace.into_iter().next().unwrap().obligation;
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index b370475f993b5..587029e9a6c65 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -21,7 +21,7 @@ use middle::const_val::ConstEvalErr;
 use middle::region;
 use middle::free_region::FreeRegionMap;
 use ty::subst::Substs;
-use ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable, ToPredicate};
+use ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable, ToPredicate, ToPredicateAtom};
 use ty::error::{ExpectedFound, TypeError};
 use infer::{InferCtxt};
 
@@ -86,6 +86,7 @@ pub struct Obligation<'tcx, T> {
 }
 
 pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
+pub type PredicateAtomObligation<'tcx> = Obligation<'tcx, ty::PredicateAtom<'tcx>>;
 pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitRef<'tcx>>;
 pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitRef<'tcx>>;
 
@@ -234,7 +235,7 @@ pub enum SelectionError<'tcx> {
 }
 
 pub struct FulfillmentError<'tcx> {
-    pub obligation: PredicateObligation<'tcx>,
+    pub obligation: PredicateAtomObligation<'tcx>,
     pub code: FulfillmentErrorCode<'tcx>
 }
 
@@ -429,11 +430,10 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
         param_env,
         cause: ObligationCause::misc(span, ast::DUMMY_NODE_ID),
         recursion_depth: 0,
-        predicate: trait_ref.to_predicate(),
+        predicate: trait_ref.to_predicate_atom(),
     };
 
-    let result = SelectionContext::new(infcx)
-        .evaluate_obligation_conservatively(&obligation);
+    let result = SelectionContext::new(infcx).evaluate_obligation_conservatively(&obligation);
     debug!("type_known_to_meet_ty={:?} bound={} => {:?}",
            ty, infcx.tcx.item_path_str(def_id), result);
 
@@ -715,6 +715,18 @@ fn vtable_methods<'a, 'tcx>(
     )
 }
 
+impl<'tcx> PredicateObligation<'tcx> {
+    pub fn from<O>(cause: ObligationCause<'tcx>,
+                   param_env: ty::ParamEnv<'tcx>,
+                   predicate: O)
+                   -> Self
+        where O: ToPredicate<'tcx>
+    {
+        let predicate = predicate.to_predicate();
+        Obligation { cause, param_env, recursion_depth: 0, predicate }
+    }
+}
+
 impl<'tcx,O> Obligation<'tcx,O> {
     pub fn new(cause: ObligationCause<'tcx>,
                param_env: ty::ParamEnv<'tcx>,
@@ -823,7 +835,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
 }
 
 impl<'tcx> FulfillmentError<'tcx> {
-    fn new(obligation: PredicateObligation<'tcx>,
+    fn new(obligation: PredicateAtomObligation<'tcx>,
            code: FulfillmentErrorCode<'tcx>)
            -> FulfillmentError<'tcx>
     {
diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs
index 4dffb58d631bc..f871892429c96 100644
--- a/src/librustc/traits/object_safety.rs
+++ b/src/librustc/traits/object_safety.rs
@@ -85,6 +85,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn astconv_object_safety_violations(self, trait_def_id: DefId)
                                             -> Vec<ObjectSafetyViolation>
     {
+        debug!("astconv_object_safety_violations(trait_def_id={:?})", trait_def_id);
+
         let mut violations = vec![];
 
         for def_id in traits::supertrait_def_ids(self, trait_def_id) {
@@ -93,7 +95,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             }
         }
 
-        debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}",
+        debug!("astconv_object_safety_violations: trait_def_id={:?} violations={:?}",
                trait_def_id,
                violations);
 
@@ -155,21 +157,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         predicates
             .predicates
             .into_iter()
-            .map(|predicate| predicate.subst_supertrait(self, &trait_ref))
+            .map(|predicate| predicate.subst_supertrait(self, trait_ref))
             .any(|predicate| {
-                match predicate {
-                    ty::Predicate::Trait(ref data) => {
+                // We can safely skip binders because `Self` type would not be bound by them.
+                match predicate.skip_binders() {
+                    ty::PredicateAtom::Trait(ref data) => {
                         // In the case of a trait predicate, we can skip the "self" type.
-                        data.skip_binder().input_types().skip(1).any(|t| t.has_self_ty())
+                        data.input_types().skip(1).any(|t| t.has_self_ty())
                     }
-                    ty::Predicate::Projection(..) |
-                    ty::Predicate::WellFormed(..) |
-                    ty::Predicate::ObjectSafe(..) |
-                    ty::Predicate::TypeOutlives(..) |
-                    ty::Predicate::RegionOutlives(..) |
-                    ty::Predicate::ClosureKind(..) |
-                    ty::Predicate::Subtype(..) |
-                    ty::Predicate::ConstEvaluatable(..) => {
+                    ty::PredicateAtom::Projection(..) |
+                    ty::PredicateAtom::WellFormed(..) |
+                    ty::PredicateAtom::ObjectSafe(..) |
+                    ty::PredicateAtom::TypeOutlives(..) |
+                    ty::PredicateAtom::RegionOutlives(..) |
+                    ty::PredicateAtom::ClosureKind(..) |
+                    ty::PredicateAtom::Subtype(..) |
+                    ty::PredicateAtom::ConstEvaluatable(..) => {
                         false
                     }
                 }
@@ -183,7 +186,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     fn generics_require_sized_self(self, def_id: DefId) -> bool {
         let sized_def_id = match self.lang_items().sized_trait() {
             Some(def_id) => def_id,
-            None => { return false; /* No Sized trait, can't require it! */ }
+            None => return false, // No Sized trait, can't require it!
         };
 
         // Search for a predicate like `Self : Sized` amongst the trait bounds.
@@ -191,19 +194,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         let predicates = predicates.instantiate_identity(self).predicates;
         elaborate_predicates(self, predicates)
             .any(|predicate| {
-                match predicate {
-                    ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => {
-                        trait_pred.0.self_ty().is_self()
-                    }
-                    ty::Predicate::Projection(..) |
-                    ty::Predicate::Trait(..) |
-                    ty::Predicate::Subtype(..) |
-                    ty::Predicate::RegionOutlives(..) |
-                    ty::Predicate::WellFormed(..) |
-                    ty::Predicate::ObjectSafe(..) |
-                    ty::Predicate::ClosureKind(..) |
-                    ty::Predicate::TypeOutlives(..) |
-                    ty::Predicate::ConstEvaluatable(..) => {
+                // We can skip the binder here because our test is not
+                // related to bound regions.
+                match predicate.skip_binders() {
+                    ty::PredicateAtom::Trait(trait_ref) =>
+                        trait_ref.def_id == sized_def_id && trait_ref.self_ty().is_self(),
+                    ty::PredicateAtom::Projection(..) |
+                    ty::PredicateAtom::Subtype(..) |
+                    ty::PredicateAtom::RegionOutlives(..) |
+                    ty::PredicateAtom::WellFormed(..) |
+                    ty::PredicateAtom::ObjectSafe(..) |
+                    ty::PredicateAtom::ClosureKind(..) |
+                    ty::PredicateAtom::TypeOutlives(..) |
+                    ty::PredicateAtom::ConstEvaluatable(..) => {
                         false
                     }
                 }
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index a463d3727c54d..917bad0ae0290 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -25,14 +25,16 @@ use super::VtableImplData;
 use super::util;
 
 use hir::def_id::DefId;
-use infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
+use infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime};
 use infer::type_variable::TypeVariableOrigin;
 use middle::const_val::ConstVal;
 use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
 use syntax::symbol::Symbol;
-use ty::subst::{Subst, Substs};
+use ty::subst::Substs;
 use ty::{self, ToPredicate, Ty, TyCtxt};
+use ty::error::TypeError;
 use ty::fold::{TypeFoldable, TypeFolder};
+use ty::subst::Subst;
 use util::common::FN_OUTPUT_NAME;
 
 /// Depending on the stage of compilation, we want projection to be
@@ -77,9 +79,6 @@ pub enum Reveal {
     All,
 }
 
-pub type PolyProjectionObligation<'tcx> =
-    Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
-
 pub type ProjectionObligation<'tcx> =
     Obligation<'tcx, ty::ProjectionPredicate<'tcx>>;
 
@@ -118,37 +117,12 @@ struct ProjectionTyCandidateSet<'tcx> {
     ambiguous: bool
 }
 
-/// Evaluates constraints of the form:
-///
-///     for<...> <T as Trait>::U == V
-///
-/// If successful, this may result in additional obligations. Also returns
-/// the projection cache key used to track these additional obligations.
-pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
-    selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
-    obligation: &PolyProjectionObligation<'tcx>)
-    -> Result<Option<Vec<PredicateObligation<'tcx>>>,
-              MismatchedProjectionTypes<'tcx>>
-{
-    debug!("poly_project_and_unify_type(obligation={:?})",
-           obligation);
-
-    let infcx = selcx.infcx();
-    infcx.commit_if_ok(|_snapshot| {
-        let (skol_predicate, param_env, _skol_map) =
-            infcx.skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
-
-        let skol_obligation = obligation.with(skol_predicate).with_env(param_env);
-        project_and_unify_type(selcx, &skol_obligation)
-    })
-}
-
 /// Evaluates constraints of the form:
 ///
 ///     <T as Trait>::U == V
 ///
 /// If successful, this may result in additional obligations.
-fn project_and_unify_type<'cx, 'gcx, 'tcx>(
+pub fn project_and_unify_type<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionObligation<'tcx>)
     -> Result<Option<Vec<PredicateObligation<'tcx>>>,
@@ -615,7 +589,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx,
     let mut obligations: Vec<_> =
         result.obligations
               .iter()
-              .filter(|obligation| match obligation.predicate {
+              .filter(|obligation| match obligation.predicate.skip_binders() {
                   // We found a `T: Foo<X = U>` predicate, let's check
                   // if `U` references any unresolved type
                   // variables. In principle, we only care if this
@@ -625,8 +599,8 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx,
                   // indirect obligations (e.g., we project to `?0`,
                   // but we have `T: Foo<X = ?1>` and `?1: Bar<X =
                   // ?0>`).
-                  ty::Predicate::Projection(ref data) =>
-                      infcx.any_unresolved_type_vars(&data.ty()),
+                  ty::PredicateAtom::Projection(ref data) =>
+                      infcx.any_unresolved_type_vars(&data.ty),
 
                   // We are only interested in `T: Foo<X = U>` predicates, whre
                   // `U` references one of `unresolved_type_vars`. =)
@@ -785,7 +759,6 @@ fn project_type<'cx, 'gcx, 'tcx>(
 
     assemble_candidates_from_param_env(selcx,
                                        obligation,
-                                       obligation_trait_ref,
                                        &mut candidates);
 
     assemble_candidates_from_trait_def(selcx,
@@ -872,13 +845,11 @@ fn project_type<'cx, 'gcx, 'tcx>(
 fn assemble_candidates_from_param_env<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: ty::TraitRef<'tcx>,
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
 {
     debug!("assemble_candidates_from_param_env(..)");
     assemble_candidates_from_predicates(selcx,
                                         obligation,
-                                        obligation_trait_ref,
                                         candidate_set,
                                         ProjectionTyCandidate::ParamEnv,
                                         obligation.param_env.caller_bounds.iter().cloned());
@@ -924,7 +895,6 @@ fn assemble_candidates_from_trait_def<'cx, 'gcx, 'tcx>(
     let bounds = elaborate_predicates(tcx, bounds.predicates);
     assemble_candidates_from_predicates(selcx,
                                         obligation,
-                                        obligation_trait_ref,
                                         candidate_set,
                                         ProjectionTyCandidate::TraitDef,
                                         bounds)
@@ -933,7 +903,6 @@ fn assemble_candidates_from_trait_def<'cx, 'gcx, 'tcx>(
 fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: ty::TraitRef<'tcx>,
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
     ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>,
     env_predicates: I)
@@ -942,37 +911,78 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
     debug!("assemble_candidates_from_predicates(obligation={:?})",
            obligation);
     let infcx = selcx.infcx();
-    for predicate in env_predicates {
-        debug!("assemble_candidates_from_predicates: predicate={:?}",
-               predicate);
-        match predicate {
-            ty::Predicate::Projection(ref data) => {
-                let same_def_id =
-                    data.0.projection_ty.item_def_id == obligation.predicate.item_def_id;
-
-                let is_match = same_def_id && infcx.probe(|_| {
-                    let data_poly_trait_ref =
-                        data.to_poly_trait_ref(infcx.tcx);
-                    infcx.at(&obligation.cause, obligation.param_env)
-                         .instantiable_as(data_poly_trait_ref, obligation_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={:?} \
-                                                             is_match={} same_def_id={}",
-                       data, is_match, same_def_id);
-
-                if is_match {
-                    candidate_set.vec.push(ctor(data.clone()));
-                }
+
+    let env_predicates = env_predicates.into_iter()
+                                       .filter_map(|p| filter_env_predicate(infcx, obligation, p))
+                                       .filter(|&p| can_unify_env_predicate(infcx, obligation, p));
+    for env_predicate in env_predicates {
+        infcx.probe(|_| {
+            if unify_env_predicate(infcx, obligation, env_predicate).is_ok() {
+                debug!("assemble_candidates_from_predicates: env_predicate={:?} matched",
+                       env_predicate);
+                candidate_set.vec.push(ctor(env_predicate));
             }
-            _ => { }
+        });
+    }
+}
+
+/// Quickly tests whether a given predicate from the environment MAY
+/// satisfy `obligation`. If so, returns `Some(_)` with a
+/// poly-projection.
+fn filter_env_predicate<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    env_predicate: ty::Predicate<'tcx>)
+    -> Option<ty::PolyProjectionPredicate<'tcx>>
+{
+    if let Some(env_projection) = env_predicate.poly_projection(infcx.tcx) {
+        if obligation.predicate.item_def_id == env_projection.item_def_id() {
+            return Some(env_projection);
         }
     }
+    None
+}
+
+/// Tests whether we can unify `env_predicate` to satisfy `obligation`.
+fn can_unify_env_predicate<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    env_projection: ty::PolyProjectionPredicate<'tcx>)
+    -> bool
+{
+    infcx.probe(|_| unify_env_predicate(infcx, obligation, env_projection).is_ok())
+}
+
+/// Uses `env_predicate` to satisfy `obligation`, if possible.
+fn unify_env_predicate<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    env_projection: ty::PolyProjectionPredicate<'tcx>)
+    -> InferResult<'tcx, Ty<'tcx>>
+{
+    // Quick sanity check.
+    if obligation.predicate.item_def_id != env_projection.item_def_id() {
+        return Err(TypeError::Mismatch);
+    }
+
+    // The `env_projection` may be wrapped in binders. Instantiate
+    // those binders with variables and then extract the results.
+    let (ty::ProjectionPredicate { projection_ty: env_projection_ty, ty: env_ty }, _) =
+        infcx.replace_late_bound_regions_with_fresh_var(
+            obligation.cause.span,
+            obligation.param_env.universe,
+            LateBoundRegionConversionTime::HigherRankedType,
+            &env_projection);
+
+    // In the environment we have `<T0 as Foo>::Bar == U`.  We are
+    // normalizing `<T1 as Foo>::Bar`. So unify `<T0 as Foo>::Bar` and
+    // `<T1 as Foo>::Bar` (really, it's just the input types that
+    // matter; the trait/associated item have already been checked by
+    // `fast_reject_env_predicate`). Once that is done, we can return
+    // `U` as our result.
+    let InferOk { value: _, obligations } = infcx.at(&obligation.cause, obligation.param_env)
+                                                 .eq(env_projection_ty, obligation.predicate)?;
+    Ok(InferOk { value: env_ty, obligations })
 }
 
 fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
@@ -1184,6 +1194,7 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
     obligation_trait_ref: ty::TraitRef<'tcx>)
     -> Progress<'tcx>
 {
+    let infcx = selcx.infcx();
     let self_ty = obligation_trait_ref.self_ty();
     let object_ty = selcx.infcx().shallow_resolve(self_ty);
     debug!("confirm_object_candidate(object_ty={:?})",
@@ -1197,49 +1208,26 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
                 object_ty)
         }
     };
-    let env_predicates = data.projection_bounds().map(|p| {
-        p.with_self_ty(selcx.tcx(), object_ty).to_predicate()
-    }).collect();
-    let env_predicate = {
-        let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates);
-
-        // select only those projections that are actually projecting an
-        // item with the correct name
-        let env_predicates = env_predicates.filter_map(|p| match p {
-            ty::Predicate::Projection(data) =>
-                if data.0.projection_ty.item_def_id == obligation.predicate.item_def_id {
-                    Some(data)
-                } else {
-                    None
-                },
-            _ => None
-        });
 
-        // select those with a relevant trait-ref
-        let mut env_predicates = env_predicates.filter(|data| {
-            let data_poly_trait_ref = data.to_poly_trait_ref(selcx.tcx());
-            selcx.infcx().probe(|_| {
-                selcx.infcx().at(&obligation.cause, obligation.param_env)
-                             .instantiable_as(data_poly_trait_ref, obligation_trait_ref)
-                             .is_ok()
-            })
-        });
+    let unelaborated_env_predicates: Vec<_> =
+        data.projection_bounds()
+            .map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate())
+            .collect();
 
-        // select the first matching one; there really ought to be one or
-        // else the object type is not WF, since an object type should
-        // include all of its projections explicitly
-        match env_predicates.next() {
-            Some(env_predicate) => env_predicate,
-            None => {
-                debug!("confirm_object_candidate: no env-predicate \
-                        found in object type `{:?}`; ill-formed",
-                       object_ty);
-                return Progress::error(selcx.tcx());
-            }
-        }
-    };
+    // select on those predicates that can be unified
+    let mut env_predicates =
+        elaborate_predicates(selcx.tcx(), unelaborated_env_predicates)
+            .filter_map(|p| filter_env_predicate(infcx, obligation, p))
+            .filter(|&p| can_unify_env_predicate(infcx, obligation, p));
 
-    confirm_param_env_candidate(selcx, obligation, env_predicate)
+    // take the first one:
+    if let Some(env_predicate) = env_predicates.next() {
+        confirm_param_env_candidate(selcx, obligation, env_predicate)
+    } else {
+        debug!("confirm_object_candidate: no env-predicate found in object type `{:?}`; ill-formed",
+               object_ty);
+        return Progress::error(selcx.tcx());
+    }
 }
 
 fn confirm_generator_candidate<'cx, 'gcx, 'tcx>(
@@ -1387,38 +1375,24 @@ fn confirm_callable_candidate<'cx, 'gcx, 'tcx>(
 fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    poly_cache_entry: ty::PolyProjectionPredicate<'tcx>)
+    env_projection: ty::PolyProjectionPredicate<'tcx>)
     -> Progress<'tcx>
 {
-    let infcx = selcx.infcx();
-    let cause = &obligation.cause;
-    let param_env = obligation.param_env;
-
-    let (cache_entry, _skol_map) =
-        infcx.replace_late_bound_regions_with_fresh_var(
-            cause.span,
-            param_env.universe,
-            LateBoundRegionConversionTime::HigherRankedType,
-            &poly_cache_entry);
-
-    let cache_trait_ref = cache_entry.projection_ty.trait_ref(infcx.tcx);
-    let obligation_trait_ref = obligation.predicate.trait_ref(infcx.tcx);
-    match infcx.at(cause, param_env).eq(cache_trait_ref, obligation_trait_ref) {
-        Ok(InferOk { value: _, obligations }) => {
+    match unify_env_predicate(selcx.infcx(), obligation, env_projection) {
+        Ok(InferOk { value, obligations }) =>
             Progress {
-                ty: cache_entry.ty,
+                ty: value,
                 obligations,
-            }
-        }
-        Err(e) => {
+            },
+
+        Err(e) =>
             span_bug!(
                 obligation.cause.span,
                 "Failed to unify obligation `{:?}` \
                  with poly_projection `{:?}`: {:?}",
                 obligation,
-                poly_cache_entry,
-                e);
-        }
+                env_projection,
+                e),
     }
 }
 
@@ -1546,21 +1520,24 @@ pub struct ProjectionCacheKey<'tcx> {
 }
 
 impl<'cx, 'gcx, 'tcx> ProjectionCacheKey<'tcx> {
-    pub fn from_poly_projection_predicate(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
-                                          predicate: &ty::PolyProjectionPredicate<'tcx>)
-                                          -> Option<Self>
+    pub fn from_projection_predicate(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
+                                     predicate: &ty::ProjectionPredicate<'tcx>)
+                                     -> Option<Self>
     {
         let infcx = selcx.infcx();
         // We don't do cross-snapshot caching of obligations with escaping regions,
         // so there's no cache key to use
-        infcx.tcx.no_late_bound_regions(&predicate)
-            .map(|predicate| ProjectionCacheKey {
+        if !predicate.has_escaping_regions() {
+            Some(ProjectionCacheKey {
                 // We don't attempt to match up with a specific type-variable state
                 // from a specific call to `opt_normalize_projection_type` - if
                 // there's no precise match, the original cache entry is "stranded"
                 // anyway.
                 ty: infcx.resolve_type_vars_if_possible(&predicate.projection_ty)
             })
+        } else {
+            None
+        }
     }
 }
 
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index a31fec8ef6ea4..ace96ed5f6ed3 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -17,7 +17,8 @@ use super::coherence;
 use super::DerivedObligationCause;
 use super::project;
 use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
-use super::{PredicateObligation, PolyTraitObligation, TraitObligation, ObligationCause};
+use super::{PredicateObligation, PredicateAtomObligation, PolyTraitObligation};
+use super::{TraitObligation, ObligationCause};
 use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
 use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
 use super::{ObjectCastObligation, Obligation};
@@ -35,7 +36,7 @@ use hir::def_id::DefId;
 use infer;
 use infer::{InferCtxt, InferOk, TypeFreshener};
 use ty::subst::{Kind, Subst, Substs};
-use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use ty::{self, ToPredicate, ToPredicateAtom, Ty, TyCtxt, TypeFoldable};
 use ty::fast_reject;
 use ty::relate::TypeRelation;
 use middle::lang_items;
@@ -569,14 +570,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// and returns `false` if not certain. However, this is not entirely
     /// accurate if inference variables are involved.
     pub fn evaluate_obligation_conservatively(&mut self,
-                                              obligation: &PredicateObligation<'tcx>)
+                                              obligation: &PredicateAtomObligation<'tcx>)
                                               -> bool
     {
         debug!("evaluate_obligation_conservatively({:?})",
                obligation);
 
         self.probe(|this, _| {
-            this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
+            this.evaluate_predicate_atom_recursively(TraitObligationStackList::empty(), obligation)
                 == EvaluatedToOk
         })
     }
@@ -609,19 +610,28 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn evaluate_predicate_recursively<'o>(&mut self,
                                           previous_stack: TraitObligationStackList<'o, 'tcx>,
                                           obligation: &PredicateObligation<'tcx>)
-                                           -> EvaluationResult
+                                          -> EvaluationResult
+    {
+        let obligation = self.infcx().skolemize_predicate_obligation(obligation);
+        self.evaluate_predicate_atom_recursively(previous_stack, &obligation)
+    }
+
+    fn evaluate_predicate_atom_recursively<'o>(&mut self,
+                                               previous_stack: TraitObligationStackList<'o, 'tcx>,
+                                               obligation: &PredicateAtomObligation<'tcx>)
+                                               -> EvaluationResult
     {
         debug!("evaluate_predicate_recursively({:?})",
                obligation);
 
         match obligation.predicate {
-            ty::Predicate::Trait(ref t) => {
+            ty::PredicateAtom::Trait(t) => {
                 assert!(!t.has_escaping_regions());
                 let obligation = obligation.with(t.clone());
                 self.evaluate_trait_predicate_recursively(previous_stack, obligation)
             }
 
-            ty::Predicate::Subtype(ref p) => {
+            ty::PredicateAtom::Subtype(p) => {
                 // does this code ever run?
                 match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) {
                     Some(Ok(InferOk { obligations, .. })) => {
@@ -633,11 +643,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 }
             }
 
-            ty::Predicate::WellFormed(ty) => {
+            ty::PredicateAtom::WellFormed(ty) => {
                 match ty::wf::obligations(self.infcx,
                                           obligation.param_env,
                                           obligation.cause.body_id,
-                                          ty, obligation.cause.span) {
+                                          ty,
+                                          obligation.cause.span) {
                     Some(obligations) =>
                         self.evaluate_predicates_recursively(previous_stack, &obligations),
                     None =>
@@ -645,13 +656,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 }
             }
 
-            ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => {
+            ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::RegionOutlives(..) => {
                 // we do not consider region relationships when
                 // evaluating trait matches
                 EvaluatedToOk
             }
 
-            ty::Predicate::ObjectSafe(trait_def_id) => {
+            ty::PredicateAtom::ObjectSafe(trait_def_id) => {
                 if self.tcx().is_object_safe(trait_def_id) {
                     EvaluatedToOk
                 } else {
@@ -659,14 +670,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 }
             }
 
-            ty::Predicate::Projection(ref data) => {
-                let project_obligation = obligation.with(data.clone());
-                match project::poly_project_and_unify_type(self, &project_obligation) {
+            ty::PredicateAtom::Projection(data) => {
+                let project_obligation = obligation.with(data);
+                match project::project_and_unify_type(self, &project_obligation) {
                     Ok(Some(subobligations)) => {
                         let result = self.evaluate_predicates_recursively(previous_stack,
                                                                           subobligations.iter());
                         if let Some(key) =
-                            ProjectionCacheKey::from_poly_projection_predicate(self, data)
+                            ProjectionCacheKey::from_projection_predicate(self, &data)
                         {
                             self.infcx.projection_cache.borrow_mut().complete(key);
                         }
@@ -681,7 +692,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 }
             }
 
-            ty::Predicate::ClosureKind(closure_def_id, kind) => {
+            ty::PredicateAtom::ClosureKind(closure_def_id, kind) => {
                 match self.infcx.closure_kind(closure_def_id) {
                     Some(closure_kind) => {
                         if closure_kind.extends(kind) {
@@ -696,7 +707,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 }
             }
 
-            ty::Predicate::ConstEvaluatable(def_id, substs) => {
+            ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
                 match self.tcx().lift_to_global(&(obligation.param_env, substs)) {
                     Some((param_env, substs)) => {
                         match self.tcx().const_eval(param_env.and((def_id, substs))) {
@@ -715,26 +726,20 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     fn evaluate_trait_predicate_recursively<'o>(&mut self,
                                                 previous_stack: TraitObligationStackList<'o, 'tcx>,
-                                                mut poly_obligation: PolyTraitObligation<'tcx>)
+                                                mut obligation: TraitObligation<'tcx>)
                                                 -> EvaluationResult
     {
         debug!("evaluate_trait_predicate_recursively({:?})",
-               poly_obligation);
+               obligation);
 
-        if !self.intercrate && poly_obligation.is_global() {
+        if !self.intercrate && obligation.is_global() {
             // If a param env is consistent, global obligations do not depend on its particular
             // value in order to work, so we can clear out the param env and get better
             // caching. (If the current param env is inconsistent, we don't care what happens).
-            debug!("evaluate_trait_predicate_recursively({:?}) - in global", poly_obligation);
-            poly_obligation.param_env = ty::ParamEnv::empty(poly_obligation.param_env.reveal);
+            debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation);
+            obligation.param_env = ty::ParamEnv::empty(obligation.param_env.reveal);
         }
 
-        let (trait_ref, param_env, _skol_map) =
-            self.infcx().skolemize_late_bound_regions(poly_obligation.param_env,
-                                                      &poly_obligation.predicate);
-
-        let obligation = poly_obligation.with(trait_ref).with_env(param_env);
-
         let stack = self.push_stack(previous_stack, &obligation);
         let fresh_trait_ref = stack.fresh_trait_ref;
         if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) {
@@ -843,7 +848,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             debug!("evaluate_stack({:?}) --> recursive",
                    stack.fresh_trait_ref);
             let cycle = stack.iter().skip(1).take(rec_index+1);
-            let cycle = cycle.map(|stack| stack.obligation.predicate.to_predicate());
+            let cycle = cycle.map(|stack| stack.obligation.predicate.to_predicate_atom());
             if self.coinductive_match(cycle) {
                 debug!("evaluate_stack({:?}) --> recursive, coinductive",
                        stack.fresh_trait_ref);
@@ -870,16 +875,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     /// - all the predicates at positions `X..` between `X` an the top are
     ///   also defaulted traits.
     pub fn coinductive_match<I>(&mut self, cycle: I) -> bool
-        where I: Iterator<Item=ty::Predicate<'tcx>>
+        where I: Iterator<Item=ty::PredicateAtom<'tcx>>
     {
         let mut cycle = cycle;
         cycle.all(|predicate| self.coinductive_predicate(predicate))
     }
 
-    fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
+    fn coinductive_predicate(&self, predicate: ty::PredicateAtom<'tcx>) -> bool {
         let result = match predicate {
-            ty::Predicate::Trait(ref data) => {
-                self.tcx().trait_is_auto(data.def_id())
+            ty::PredicateAtom::Trait(ref data) => {
+                self.tcx().trait_is_auto(data.def_id)
             }
             _ => {
                 false
@@ -2587,10 +2592,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                                 obligation.predicate,
                                                 trait_ref)?);
 
-        obligations.push(Obligation::new(
+        obligations.push(Obligation::with_depth(
             obligation.cause.clone(),
+            obligation.recursion_depth,
             obligation.param_env,
-            ty::Predicate::ClosureKind(closure_def_id, kind)));
+            ty::PredicateAtom::ClosureKind(closure_def_id, kind).to_predicate()));
 
         Ok(VtableClosureData {
             closure_def_id,
@@ -3106,7 +3112,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         };
 
         // Filter down to trait-ref bounds:
-        let bounds = bounds.filter_map(move |o| o.to_opt_poly_trait_ref());
+        let tcx = self.tcx();
+        let bounds = bounds.filter_map(move |o| o.poly_trait(tcx));
 
         // Micro-optimization: filter out predicates relating to different
         // traits.
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index 685252764f0cc..c2aeb8f6c9031 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -393,7 +393,7 @@ fn to_pretty_impl_header(tcx: TyCtxt, impl_def_id: DefId) -> Option<String> {
     let predicates = tcx.predicates_of(impl_def_id).predicates;
     let mut pretty_predicates = Vec::with_capacity(predicates.len());
     for p in predicates {
-        if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() {
+        if let Some(poly_trait_ref) = p.poly_trait(tcx) {
             if Some(poly_trait_ref.def_id()) == sized_trait {
                 types_without_default_bounds.remove(poly_trait_ref.self_ty());
                 continue;
diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs
index f5fe133ef4d56..1a16fceca8eb7 100644
--- a/src/librustc/traits/util.rs
+++ b/src/librustc/traits/util.rs
@@ -10,7 +10,7 @@
 
 use hir::def_id::DefId;
 use ty::subst::{Subst, Substs};
-use ty::{self, Ty, TyCtxt, ToPredicate};
+use ty::{self, ToPredicate, ToPredicateAtom, Ty, TyCtxt};
 use ty::outlives::Component;
 use util::nodemap::FxHashSet;
 use hir::{self};
@@ -22,36 +22,14 @@ fn anonymize_predicate<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                        pred: &ty::Predicate<'tcx>)
                                        -> ty::Predicate<'tcx> {
     match *pred {
-        ty::Predicate::Trait(ref data) =>
-            ty::Predicate::Trait(tcx.anonymize_late_bound_regions(data)),
+        ty::Predicate::Poly(ref binder) =>
+            ty::Predicate::Poly(tcx.anonymize_late_bound_regions(binder)),
 
-        ty::Predicate::RegionOutlives(ref data) =>
-            ty::Predicate::RegionOutlives(tcx.anonymize_late_bound_regions(data)),
-
-        ty::Predicate::TypeOutlives(ref data) =>
-            ty::Predicate::TypeOutlives(tcx.anonymize_late_bound_regions(data)),
-
-        ty::Predicate::Projection(ref data) =>
-            ty::Predicate::Projection(tcx.anonymize_late_bound_regions(data)),
-
-        ty::Predicate::WellFormed(data) =>
-            ty::Predicate::WellFormed(data),
-
-        ty::Predicate::ObjectSafe(data) =>
-            ty::Predicate::ObjectSafe(data),
-
-        ty::Predicate::ClosureKind(closure_def_id, kind) =>
-            ty::Predicate::ClosureKind(closure_def_id, kind),
-
-        ty::Predicate::Subtype(ref data) =>
-            ty::Predicate::Subtype(tcx.anonymize_late_bound_regions(data)),
-
-        ty::Predicate::ConstEvaluatable(def_id, substs) =>
-            ty::Predicate::ConstEvaluatable(def_id, substs),
+        ty::Predicate::Atom(atom) =>
+            ty::Predicate::Atom(atom),
     }
 }
 
-
 struct PredicateSet<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
     set: FxHashSet<ty::Predicate<'tcx>>,
@@ -123,115 +101,96 @@ pub fn elaborate_predicates<'cx, 'gcx, 'tcx>(
 }
 
 impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
-    pub fn filter_to_traits(self) -> FilterToTraits<Self> {
-        FilterToTraits::new(self)
+    pub fn filter_to_traits(self) -> FilterToTraits<'cx, 'gcx, 'tcx> {
+        FilterToTraits::new(self.visited.tcx, self)
     }
 
     fn push(&mut self, predicate: &ty::Predicate<'tcx>) {
         let tcx = self.visited.tcx;
-        match *predicate {
-            ty::Predicate::Trait(ref data) => {
-                // Predicates declared on the trait.
-                let predicates = tcx.super_predicates_of(data.def_id());
-
-                let mut predicates: Vec<_> =
-                    predicates.predicates
-                              .iter()
-                              .map(|p| p.subst_supertrait(tcx, data))
-                              .collect();
-
-                debug!("super_predicates: data={:?} predicates={:?}",
-                       data, predicates);
-
-                // Only keep those bounds that we haven't already
-                // seen.  This is necessary to prevent infinite
-                // recursion in some cases.  One common case is when
-                // people define `trait Sized: Sized { }` rather than `trait
-                // Sized { }`.
-                predicates.retain(|r| self.visited.insert(r));
-
-                self.stack.extend(predicates);
-            }
-            ty::Predicate::WellFormed(..) => {
-                // Currently, we do not elaborate WF predicates,
-                // although we easily could.
-            }
-            ty::Predicate::ObjectSafe(..) => {
-                // Currently, we do not elaborate object-safe
-                // predicates.
-            }
-            ty::Predicate::Subtype(..) => {
-                // Currently, we do not "elaborate" predicates like `X
-                // <: Y`, though conceivably we might.
-            }
-            ty::Predicate::Projection(..) => {
-                // Nothing to elaborate in a projection predicate.
-            }
-            ty::Predicate::ClosureKind(..) => {
-                // Nothing to elaborate when waiting for a closure's kind to be inferred.
-            }
-            ty::Predicate::ConstEvaluatable(..) => {
-                // Currently, we do not elaborate const-evaluatable
-                // predicates.
-            }
 
-            ty::Predicate::RegionOutlives(..) => {
-                // Nothing to elaborate from `'a: 'b`.
-            }
-
-            ty::Predicate::TypeOutlives(ref data) => {
-                // We know that `T: 'a` for some type `T`. We can
-                // often elaborate this. For example, if we know that
-                // `[U]: 'a`, that implies that `U: 'a`. Similarly, if
-                // we know `&'a U: 'b`, then we know that `'a: 'b` and
-                // `U: 'b`.
-                //
-                // We can basically ignore bound regions here. So for
-                // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to
-                // `'a: 'b`.
-
-                // Ignore `for<'a> T: 'a` -- we might in the future
-                // consider this as evidence that `T: 'static`, but
-                // I'm a bit wary of such constructions and so for now
-                // I want to be conservative. --nmatsakis
-                let ty_max = data.skip_binder().0;
-                let r_min = data.skip_binder().1;
-                if r_min.is_late_bound() {
-                    return;
-                }
+        // We don't elaborate all kinds of predicates. Also, the
+        // treatment of binders can be a touch tricky here (in
+        // particular, we can't just skolemize and recurse). So don't
+        // do a general match on the kind of predicate, instead try to
+        // convert to more narrow poly-things.
+
+        // Elaborate `T: Foo` to include where-clauses declared on trait `Foo`.
+        if let Some(data) = predicate.poly_trait(tcx) {
+            let predicates = tcx.super_predicates_of(data.def_id());
+
+            let mut predicates: Vec<_> =
+                predicates.predicates
+                          .iter()
+                          .map(|p| p.subst_supertrait(tcx, data))
+                          .collect();
+
+            debug!("super_predicates: data={:?} predicates={:?}",
+                   data, predicates);
+
+            // Only keep those bounds that we haven't already
+            // seen.  This is necessary to prevent infinite
+            // recursion in some cases.  One common case is when
+            // people define `trait Sized: Sized { }` rather than `trait
+            // Sized { }`.
+            predicates.retain(|r| self.visited.insert(r));
+
+            self.stack.extend(predicates);
+        }
 
-                let visited = &mut self.visited;
-                self.stack.extend(
-                    tcx.outlives_components(ty_max)
-                       .into_iter()
-                       .filter_map(|component| match component {
-                           Component::Region(r) => if r.is_late_bound() {
-                               None
-                           } else {
-                               Some(ty::Predicate::RegionOutlives(
-                                   ty::Binder(ty::OutlivesPredicate(r, r_min))))
-                           },
-
-                           Component::Param(p) => {
-                               let ty = tcx.mk_param(p.idx, p.name);
-                               Some(ty::Predicate::TypeOutlives(
-                                   ty::Binder(ty::OutlivesPredicate(ty, r_min))))
-                           },
-
-                           Component::UnresolvedInferenceVariable(_) => {
-                               None
-                           },
-
-                           Component::Projection(_) |
-                           Component::EscapingProjection(_) => {
-                               // We can probably do more here. This
-                               // corresponds to a case like `<T as
-                               // Foo<'a>>::U: 'b`.
-                               None
-                           },
-                       })
-                       .filter(|p| visited.insert(p)));
+        // Elaborate `T: 'a` to include other outlives relationships.
+        //
+        // e.g., `&'a i32: 'b` implies that `'a: 'b`.
+        if let Some(data) = predicate.poly_type_outlives(tcx) {
+            // We know that `T: 'a` for some type `T`. We can
+            // often elaborate this. For example, if we know that
+            // `[U]: 'a`, that implies that `U: 'a`. Similarly, if
+            // we know `&'a U: 'b`, then we know that `'a: 'b` and
+            // `U: 'b`.
+            //
+            // We can basically ignore bound regions here. So for
+            // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to
+            // `'a: 'b`.
+
+            // Ignore `for<'a> T: 'a` -- we might in the future
+            // consider this as evidence that `T: 'static`, but
+            // I'm a bit wary of such constructions and so for now
+            // I want to be conservative. --nmatsakis
+            let ty_max = data.skip_binder().0;
+            let r_min = data.skip_binder().1;
+            if r_min.is_late_bound() {
+                return;
             }
+
+            let visited = &mut self.visited;
+            self.stack.extend(
+                tcx.outlives_components(ty_max)
+                   .into_iter()
+                   .filter_map(|component| match component {
+                       Component::Region(r) => if r.is_late_bound() {
+                           None
+                       } else {
+                           Some(ty::OutlivesPredicate(r, r_min).to_predicate_atom())
+                       },
+
+                       Component::Param(p) => {
+                           let ty = tcx.mk_param(p.idx, p.name);
+                           Some(ty::OutlivesPredicate(ty, r_min).to_predicate_atom())
+                       },
+
+                       Component::UnresolvedInferenceVariable(_) => {
+                           None
+                       },
+
+                       Component::Projection(_) |
+                       Component::EscapingProjection(_) => {
+                           // We can probably do more here. This
+                           // corresponds to a case like `<T as
+                           // Foo<'a>>::U: 'b`.
+                           None
+                       },
+                   })
+                   .map(|atom| atom.to_predicate())
+                   .filter(|p| visited.insert(p)));
         }
     }
 }
@@ -257,7 +216,7 @@ impl<'cx, 'gcx, 'tcx> Iterator for Elaborator<'cx, 'gcx, 'tcx> {
 // Supertrait iterator
 ///////////////////////////////////////////////////////////////////////////
 
-pub type Supertraits<'cx, 'gcx, 'tcx> = FilterToTraits<Elaborator<'cx, 'gcx, 'tcx>>;
+pub type Supertraits<'cx, 'gcx, 'tcx> = FilterToTraits<'cx, 'gcx, 'tcx>;
 
 pub fn supertraits<'cx, 'gcx, 'tcx>(tcx: TyCtxt<'cx, 'gcx, 'tcx>,
                                     trait_ref: ty::PolyTraitRef<'tcx>)
@@ -302,12 +261,13 @@ impl<'cx, 'gcx, 'tcx> Iterator for SupertraitDefIds<'cx, 'gcx, 'tcx> {
             None => { return None; }
         };
 
-        let predicates = self.tcx.super_predicates_of(def_id);
+        let tcx = self.tcx;
+        let predicates = tcx.super_predicates_of(def_id);
         let visited = &mut self.visited;
         self.stack.extend(
             predicates.predicates
                       .iter()
-                      .filter_map(|p| p.to_opt_poly_trait_ref())
+                      .filter_map(|p| p.poly_trait(tcx))
                       .map(|t| t.def_id())
                       .filter(|&super_def_id| visited.insert(super_def_id)));
         Some(def_id)
@@ -320,29 +280,30 @@ impl<'cx, 'gcx, 'tcx> Iterator for SupertraitDefIds<'cx, 'gcx, 'tcx> {
 
 /// A filter around an iterator of predicates that makes it yield up
 /// just trait references.
-pub struct FilterToTraits<I> {
-    base_iterator: I
+pub struct FilterToTraits<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    base_iterator: Elaborator<'cx, 'gcx, 'tcx>,
 }
 
-impl<I> FilterToTraits<I> {
-    fn new(base: I) -> FilterToTraits<I> {
-        FilterToTraits { base_iterator: base }
+impl<'cx, 'gcx, 'tcx> FilterToTraits<'cx, 'gcx, 'tcx> {
+    fn new(tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+           base_iterator: Elaborator<'cx, 'gcx, 'tcx>)
+           -> Self {
+        FilterToTraits { tcx, base_iterator }
     }
 }
 
-impl<'tcx,I:Iterator<Item=ty::Predicate<'tcx>>> Iterator for FilterToTraits<I> {
+impl<'cx, 'gcx, 'tcx> Iterator for FilterToTraits<'cx, 'gcx, 'tcx> {
     type Item = ty::PolyTraitRef<'tcx>;
 
     fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> {
         loop {
             match self.base_iterator.next() {
-                None => {
-                    return None;
-                }
-                Some(ty::Predicate::Trait(data)) => {
-                    return Some(data);
-                }
-                Some(_) => {
+                None => return None,
+                Some(predicate) => {
+                    if let Some(trait_ref) = predicate.poly_trait(self.tcx) {
+                        return Some(trait_ref);
+                    }
                 }
             }
         }
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs
index 149999e0eee0f..84a2af9b4635a 100644
--- a/src/librustc/ty/fold.rs
+++ b/src/librustc/ty/fold.rs
@@ -519,15 +519,20 @@ pub fn shift_region_ref<'a, 'gcx, 'tcx>(
 
 pub fn shift_regions<'a, 'gcx, 'tcx, T>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                         amount: u32,
-                                        value: &T) -> T
+                                        value: &T)
+                                        -> T
     where T: TypeFoldable<'tcx>
 {
     debug!("shift_regions(value={:?}, amount={})",
            value, amount);
 
-    value.fold_with(&mut RegionFolder::new(tcx, &mut false, &mut |region, _current_depth| {
-        shift_region_ref(tcx, region, amount)
-    }))
+    if !value.has_escaping_regions() {
+        value.clone()
+    } else {
+        value.fold_with(&mut RegionFolder::new(tcx, &mut false, &mut |region, _current_depth| {
+            shift_region_ref(tcx, region, amount)
+        }))
+    }
 }
 
 /// An "escaping region" is a bound region whose binder is not part of `t`.
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 2b2231258b617..f8e129faa7238 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -44,7 +44,6 @@ use std::iter::FromIterator;
 use std::ops::Deref;
 use std::rc::Rc;
 use std::slice;
-use std::vec::IntoIter;
 use std::mem;
 use syntax::ast::{self, DUMMY_NODE_ID, Name, Ident, NodeId};
 use syntax::attr;
@@ -847,36 +846,30 @@ impl<'a, 'gcx, 'tcx> GenericPredicates<'tcx> {
         }
         instantiated.predicates.extend(&self.predicates)
     }
-
-    pub fn instantiate_supertrait(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                                  poly_trait_ref: &ty::PolyTraitRef<'tcx>)
-                                  -> InstantiatedPredicates<'tcx>
-    {
-        assert_eq!(self.parent, None);
-        InstantiatedPredicates {
-            predicates: self.predicates.iter().map(|pred| {
-                pred.subst_supertrait(tcx, poly_trait_ref)
-            }).collect()
-        }
-    }
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum Predicate<'tcx> {
+    Poly(Binder<PredicateAtom<'tcx>>),
+    Atom(PredicateAtom<'tcx>),
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+pub enum PredicateAtom<'tcx> {
     /// Corresponds to `where Foo : Bar<A,B,C>`. `Foo` here would be
     /// the `Self` type of the trait reference and `A`, `B`, and `C`
     /// would be the type parameters.
-    Trait(PolyTraitRef<'tcx>),
+    Trait(TraitRef<'tcx>),
 
     /// where 'a : 'b
-    RegionOutlives(PolyRegionOutlivesPredicate<'tcx>),
+    RegionOutlives(RegionOutlivesPredicate<'tcx>),
 
     /// where T : 'a
-    TypeOutlives(PolyTypeOutlivesPredicate<'tcx>),
+    TypeOutlives(TypeOutlivesPredicate<'tcx>),
 
     /// where <T as TraitRef>::Name == X, approximately.
     /// See `ProjectionPredicate` struct for details.
-    Projection(PolyProjectionPredicate<'tcx>),
+    Projection(ProjectionPredicate<'tcx>),
 
     /// no syntax: T WF
     WellFormed(Ty<'tcx>),
@@ -890,20 +883,32 @@ pub enum Predicate<'tcx> {
     ClosureKind(DefId, ClosureKind),
 
     /// `T1 <: T2`
-    Subtype(PolySubtypePredicate<'tcx>),
+    Subtype(SubtypePredicate<'tcx>),
 
     /// Constant initializer must evaluate successfully.
     ConstEvaluatable(DefId, &'tcx Substs<'tcx>),
 }
 
 impl<'a, 'gcx, 'tcx> Predicate<'tcx> {
+    /// If there are any binders in this predicate, skip through them
+    /// and expose the underlying atom. Use with caution, as the depth
+    /// of late-bound-regions in this atom cannot be interpreted --
+    /// you don't know if there was a binder or not!
+    pub fn skip_binders(self) -> PredicateAtom<'tcx> {
+        match self {
+            Predicate::Poly(binder) => *binder.skip_binder(),
+            Predicate::Atom(atom) => atom,
+        }
+    }
+
     /// Performs a substitution suitable for going from a
     /// poly-trait-ref to supertraits that must hold if that
     /// poly-trait-ref holds. This is slightly different from a normal
     /// substitution in terms of what happens with bound regions.  See
     /// lengthy comment below for details.
-    pub fn subst_supertrait(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                            trait_ref: &ty::PolyTraitRef<'tcx>)
+    pub fn subst_supertrait(&self,
+                            tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                            trait_ref: ty::PolyTraitRef<'tcx>)
                             -> ty::Predicate<'tcx>
     {
         // The interaction between HRTB and supertraits is not entirely
@@ -968,24 +973,120 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> {
 
         let substs = &trait_ref.0.substs;
         match *self {
-            Predicate::Trait(ty::Binder(ref data)) =>
-                Predicate::Trait(ty::Binder(data.subst(tcx, substs))),
-            Predicate::Subtype(ty::Binder(ref data)) =>
-                Predicate::Subtype(ty::Binder(data.subst(tcx, substs))),
-            Predicate::RegionOutlives(ty::Binder(ref data)) =>
-                Predicate::RegionOutlives(ty::Binder(data.subst(tcx, substs))),
-            Predicate::TypeOutlives(ty::Binder(ref data)) =>
-                Predicate::TypeOutlives(ty::Binder(data.subst(tcx, substs))),
-            Predicate::Projection(ty::Binder(ref data)) =>
-                Predicate::Projection(ty::Binder(data.subst(tcx, substs))),
-            Predicate::WellFormed(data) =>
-                Predicate::WellFormed(data.subst(tcx, substs)),
-            Predicate::ObjectSafe(trait_def_id) =>
-                Predicate::ObjectSafe(trait_def_id),
-            Predicate::ClosureKind(closure_def_id, kind) =>
-                Predicate::ClosureKind(closure_def_id, kind),
-            Predicate::ConstEvaluatable(def_id, const_substs) =>
-                Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)),
+            Predicate::Poly(binder) =>
+                ty::Binder(binder.skip_binder().subst(tcx, substs)).to_predicate(),
+
+            Predicate::Atom(atom) =>
+                ty::Binder(atom.subst(tcx, substs)).to_predicate(),
+        }
+    }
+
+    /// Helper for selecting subsets of predicates. The `op` here is
+    /// some function that transforms a PredicateAtom into an R,
+    /// **maintaining the binding level of any late-bound regions**.
+    fn poly_map<OP, R>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, mut op: OP) -> Option<Binder<R>>
+        where OP: FnMut(PredicateAtom<'tcx>) -> Option<R>,
+              R: TypeFoldable<'tcx>,
+    {
+        match self {
+            Predicate::Poly(binder) => {
+                match op(*binder.skip_binder()) { // binder moves from here...
+                    Some(v) => {
+                        Some(ty::Binder(v)) // ...to here
+                    }
+
+                    None => None,
+                }
+            }
+            Predicate::Atom(atom) => {
+                let atom = fold::shift_regions(tcx, 1, &atom); // insert a binder here...
+                match op(atom) {
+                    Some(v) => {
+                        Some(ty::Binder(v)) // ...that we instantiate here
+                    }
+
+                    None => None,
+                }
+            }
+        }
+    }
+
+    /// If the underlying atom is a `PredicateAtom::Trait`, returns
+    /// `Some(trait_ref)`. Else None.
+    pub fn poly_trait(self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
+                      -> Option<PolyTraitRef<'tcx>> {
+        self.poly_map(tcx, |atom| atom.trait_())
+    }
+
+    /// If the underlying atom is a `PredicateAtom::Trait`, returns
+    /// `Some(trait_ref)`. Else None.
+    pub fn poly_type_outlives(self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
+                              -> Option<PolyTypeOutlivesPredicate<'tcx>> {
+        self.poly_map(tcx, |atom| atom.type_outlives())
+    }
+
+    /// If the underlying atom is a `PredicateAtom::Projection`, returns
+    /// `Some(trait_ref)`. Else None.
+    pub fn poly_projection(self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
+                           -> Option<PolyProjectionPredicate<'tcx>> {
+        self.poly_map(tcx, |atom| atom.projection())
+    }
+
+    /// Does various short-circuit checks and returns true if `self`
+    /// and `other` are obviously NOT unifiable.
+    pub fn fast_reject<O>(&self, other: O) -> bool
+        where O: ToPredicate<'tcx>
+    {
+        let other = other.to_predicate();
+        return self.skip_binders().fast_reject(other.skip_binders());
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> PredicateAtom<'tcx> {
+    /// Does various short-circuit checks and returns true if `self`
+    /// and `other` are obviously NOT unifiable.
+    pub fn fast_reject<O>(self, other: O) -> bool
+        where O: ToPredicateAtom<'tcx>
+    {
+        let other = other.to_predicate_atom();
+        return fast_reject_mono(self, other);
+
+        fn fast_reject_mono<'tcx>(a: PredicateAtom<'tcx>, b: PredicateAtom<'tcx>) -> bool {
+            match (a, b) {
+                (ty::PredicateAtom::Projection(ref a), ty::PredicateAtom::Projection(ref b)) =>
+                    a.projection_ty.item_def_id != b.projection_ty.item_def_id,
+                (ty::PredicateAtom::Trait(ref a), ty::PredicateAtom::Trait(ref b)) =>
+                    a.def_id != b.def_id,
+                _ =>
+                    mem::discriminant(&a) != mem::discriminant(&b),
+            }
+        }
+    }
+
+    /// If this is a `PredicateAtom::Trait`, returns `Some(_)`. Else None.
+    pub fn trait_(self) -> Option<TraitRef<'tcx>> {
+        if let PredicateAtom::Trait(t) = self {
+            Some(t)
+        } else {
+            None
+        }
+    }
+
+    /// If this is a `PredicateAtom::Projection`, returns `Some(_)`. Else None.
+    pub fn projection(self) -> Option<ProjectionPredicate<'tcx>> {
+        if let PredicateAtom::Projection(t) = self {
+            Some(t)
+        } else {
+            None
+        }
+    }
+
+    /// If this is a `PredicateAtom::TypeOutlives`, returns `Some(_)`. Else None.
+    pub fn type_outlives(self) -> Option<TypeOutlivesPredicate<'tcx>> {
+        if let PredicateAtom::TypeOutlives(t) = self {
+            Some(t)
+        } else {
+            None
         }
     }
 }
@@ -996,10 +1097,10 @@ pub type PolyEquatePredicate<'tcx> = ty::Binder<EquatePredicate<'tcx>>;
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct OutlivesPredicate<A,B>(pub A, pub B); // `A : B`
-pub type PolyOutlivesPredicate<A,B> = ty::Binder<OutlivesPredicate<A,B>>;
-pub type PolyRegionOutlivesPredicate<'tcx> = PolyOutlivesPredicate<ty::Region<'tcx>,
-                                                                   ty::Region<'tcx>>;
-pub type PolyTypeOutlivesPredicate<'tcx> = PolyOutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>;
+pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>;
+pub type PolyRegionOutlivesPredicate<'tcx> = Binder<RegionOutlivesPredicate<'tcx>>;
+pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>;
+pub type PolyTypeOutlivesPredicate<'tcx> = Binder<OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>;
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct SubtypePredicate<'tcx> {
@@ -1030,6 +1131,14 @@ pub struct ProjectionPredicate<'tcx> {
 pub type PolyProjectionPredicate<'tcx> = Binder<ProjectionPredicate<'tcx>>;
 
 impl<'tcx> PolyProjectionPredicate<'tcx> {
+    /// Returns the def-id of the item being projected by this
+    /// projection predicate. So if `self` were `<T as Iterator>::Item
+    /// = U`, then this method would return the def-id for
+    /// `Iterator::Item`.
+    pub fn item_def_id(&self) -> DefId {
+        self.skip_binder().projection_ty.item_def_id
+    }
+
     pub fn to_poly_trait_ref(&self, tcx: TyCtxt) -> PolyTraitRef<'tcx> {
         // Note: unlike with TraitRef::to_poly_trait_ref(),
         // self.0.trait_ref is permitted to have escaping regions.
@@ -1044,7 +1153,16 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
     }
 }
 
-pub trait ToPredicate<'tcx> {
+impl<'tcx> ProjectionPredicate<'tcx> {
+    /// Given a projection predicate like `<T0 as Trait<T1...Tn>>::Foo
+    /// = U`, returns the trait ref `T0 as Trait<T1...Tn>` from the
+    /// projection.
+    pub fn to_trait_ref(&self, tcx: TyCtxt) -> TraitRef<'tcx> {
+        self.projection_ty.trait_ref(tcx)
+    }
+}
+
+pub trait ToPredicate<'tcx>: Sized {
     fn to_predicate(self) -> Predicate<'tcx>;
 }
 
@@ -1054,96 +1172,60 @@ impl<'tcx> ToPredicate<'tcx> for Predicate<'tcx> {
     }
 }
 
-impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> {
+impl<'tcx, T> ToPredicate<'tcx> for T
+    where T: ToPredicateAtom<'tcx>
+{
     fn to_predicate(self) -> Predicate<'tcx> {
-        assert!(!self.has_escaping_regions());
-        ty::Predicate::Trait(ty::Binder(self))
+        Predicate::Atom(self.to_predicate_atom())
     }
 }
 
-impl<'tcx> ToPredicate<'tcx> for PolyTraitRef<'tcx> {
+impl<'tcx, T> ToPredicate<'tcx> for Binder<T>
+    where T: ToPredicateAtom<'tcx>
+{
     fn to_predicate(self) -> Predicate<'tcx> {
-        ty::Predicate::Trait(self)
+        let bound_atom = self.map_bound(|t| t.to_predicate_atom());
+        Predicate::Poly(bound_atom)
     }
 }
 
-impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
-    fn to_predicate(self) -> Predicate<'tcx> {
-        Predicate::RegionOutlives(self)
+pub trait ToPredicateAtom<'tcx>: Sized + TypeFoldable<'tcx> {
+    fn to_predicate_atom(self) -> PredicateAtom<'tcx>;
+}
+
+impl<'tcx> ToPredicateAtom<'tcx> for PredicateAtom<'tcx> {
+    fn to_predicate_atom(self) -> PredicateAtom<'tcx> {
+        self
     }
 }
 
-impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
-    fn to_predicate(self) -> Predicate<'tcx> {
-        Predicate::TypeOutlives(self)
+impl<'tcx> ToPredicateAtom<'tcx> for TraitRef<'tcx> {
+    fn to_predicate_atom(self) -> PredicateAtom<'tcx> {
+        PredicateAtom::Trait(self)
     }
 }
 
-impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
-    fn to_predicate(self) -> Predicate<'tcx> {
-        Predicate::Projection(self)
+impl<'tcx> ToPredicateAtom<'tcx> for RegionOutlivesPredicate<'tcx> {
+    fn to_predicate_atom(self) -> PredicateAtom<'tcx> {
+        PredicateAtom::RegionOutlives(self)
     }
 }
 
-impl<'tcx> Predicate<'tcx> {
-    /// Iterates over the types in this predicate. Note that in all
-    /// cases this is skipping over a binder, so late-bound regions
-    /// with depth 0 are bound by the predicate.
-    pub fn walk_tys(&self) -> IntoIter<Ty<'tcx>> {
-        let vec: Vec<_> = match *self {
-            ty::Predicate::Trait(ref data) => {
-                data.skip_binder().input_types().collect()
-            }
-            ty::Predicate::Subtype(ty::Binder(SubtypePredicate { a, b, a_is_expected: _ })) => {
-                vec![a, b]
-            }
-            ty::Predicate::TypeOutlives(ty::Binder(ref data)) => {
-                vec![data.0]
-            }
-            ty::Predicate::RegionOutlives(..) => {
-                vec![]
-            }
-            ty::Predicate::Projection(ref data) => {
-                data.0.projection_ty.substs.types().chain(Some(data.0.ty)).collect()
-            }
-            ty::Predicate::WellFormed(data) => {
-                vec![data]
-            }
-            ty::Predicate::ObjectSafe(_trait_def_id) => {
-                vec![]
-            }
-            ty::Predicate::ClosureKind(_closure_def_id, _kind) => {
-                vec![]
-            }
-            ty::Predicate::ConstEvaluatable(_, substs) => {
-                substs.types().collect()
-            }
-        };
+impl<'tcx> ToPredicateAtom<'tcx> for TypeOutlivesPredicate<'tcx> {
+    fn to_predicate_atom(self) -> PredicateAtom<'tcx> {
+        PredicateAtom::TypeOutlives(self)
+    }
+}
 
-        // The only reason to collect into a vector here is that I was
-        // too lazy to make the full (somewhat complicated) iterator
-        // type that would be needed here. But I wanted this fn to
-        // return an iterator conceptually, rather than a `Vec`, so as
-        // to be closer to `Ty::walk`.
-        vec.into_iter()
+impl<'tcx> ToPredicateAtom<'tcx> for ProjectionPredicate<'tcx> {
+    fn to_predicate_atom(self) -> PredicateAtom<'tcx> {
+        PredicateAtom::Projection(self)
     }
+}
 
-    pub fn to_opt_poly_trait_ref(&self) -> Option<PolyTraitRef<'tcx>> {
-        match *self {
-            Predicate::Trait(t) => {
-                Some(t)
-            }
-            Predicate::Projection(..) |
-            Predicate::Subtype(..) |
-            Predicate::RegionOutlives(..) |
-            Predicate::WellFormed(..) |
-            Predicate::ObjectSafe(..) |
-            Predicate::ClosureKind(..) |
-            Predicate::TypeOutlives(..) |
-            Predicate::ConstEvaluatable(..) => {
-                None
-            }
-        }
+impl<'tcx> ToPredicateAtom<'tcx> for SubtypePredicate<'tcx> {
+    fn to_predicate_atom(self) -> PredicateAtom<'tcx> {
+        PredicateAtom::Subtype(self)
     }
 }
 
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index a522cf785ab31..52561edeb2dd9 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -193,33 +193,45 @@ impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> {
     type Lifted = ty::Predicate<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
         match *self {
-            ty::Predicate::Trait(ref binder) => {
-                tcx.lift(binder).map(ty::Predicate::Trait)
+            ty::Predicate::Poly(ref binder) =>
+                tcx.lift(binder).map(ty::Predicate::Poly),
+            ty::Predicate::Atom(ref atom) =>
+                tcx.lift(atom).map(ty::Predicate::Atom),
+        }
+    }
+}
+
+impl<'a, 'tcx> Lift<'tcx> for ty::PredicateAtom<'a> {
+    type Lifted = ty::PredicateAtom<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        match *self {
+            ty::PredicateAtom::Trait(ref binder) => {
+                tcx.lift(binder).map(ty::PredicateAtom::Trait)
             }
-            ty::Predicate::Subtype(ref binder) => {
-                tcx.lift(binder).map(ty::Predicate::Subtype)
+            ty::PredicateAtom::Subtype(ref binder) => {
+                tcx.lift(binder).map(ty::PredicateAtom::Subtype)
             }
-            ty::Predicate::RegionOutlives(ref binder) => {
-                tcx.lift(binder).map(ty::Predicate::RegionOutlives)
+            ty::PredicateAtom::RegionOutlives(ref binder) => {
+                tcx.lift(binder).map(ty::PredicateAtom::RegionOutlives)
             }
-            ty::Predicate::TypeOutlives(ref binder) => {
-                tcx.lift(binder).map(ty::Predicate::TypeOutlives)
+            ty::PredicateAtom::TypeOutlives(ref binder) => {
+                tcx.lift(binder).map(ty::PredicateAtom::TypeOutlives)
             }
-            ty::Predicate::Projection(ref binder) => {
-                tcx.lift(binder).map(ty::Predicate::Projection)
+            ty::PredicateAtom::Projection(ref binder) => {
+                tcx.lift(binder).map(ty::PredicateAtom::Projection)
             }
-            ty::Predicate::WellFormed(ty) => {
-                tcx.lift(&ty).map(ty::Predicate::WellFormed)
+            ty::PredicateAtom::WellFormed(ty) => {
+                tcx.lift(&ty).map(ty::PredicateAtom::WellFormed)
             }
-            ty::Predicate::ClosureKind(closure_def_id, kind) => {
-                Some(ty::Predicate::ClosureKind(closure_def_id, kind))
+            ty::PredicateAtom::ClosureKind(closure_def_id, kind) => {
+                Some(ty::PredicateAtom::ClosureKind(closure_def_id, kind))
             }
-            ty::Predicate::ObjectSafe(trait_def_id) => {
-                Some(ty::Predicate::ObjectSafe(trait_def_id))
+            ty::PredicateAtom::ObjectSafe(trait_def_id) => {
+                Some(ty::PredicateAtom::ObjectSafe(trait_def_id))
             }
-            ty::Predicate::ConstEvaluatable(def_id, substs) => {
+            ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
                 tcx.lift(&substs).map(|substs| {
-                    ty::Predicate::ConstEvaluatable(def_id, substs)
+                    ty::PredicateAtom::ConstEvaluatable(def_id, substs)
                 })
             }
         }
@@ -971,38 +983,54 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice<ty::Predicate<'tcx>> {
 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 {
-            ty::Predicate::Trait(ref a) =>
-                ty::Predicate::Trait(a.fold_with(folder)),
-            ty::Predicate::Subtype(ref binder) =>
-                ty::Predicate::Subtype(binder.fold_with(folder)),
-            ty::Predicate::RegionOutlives(ref binder) =>
-                ty::Predicate::RegionOutlives(binder.fold_with(folder)),
-            ty::Predicate::TypeOutlives(ref binder) =>
-                ty::Predicate::TypeOutlives(binder.fold_with(folder)),
-            ty::Predicate::Projection(ref binder) =>
-                ty::Predicate::Projection(binder.fold_with(folder)),
-            ty::Predicate::WellFormed(data) =>
-                ty::Predicate::WellFormed(data.fold_with(folder)),
-            ty::Predicate::ClosureKind(closure_def_id, kind) =>
-                ty::Predicate::ClosureKind(closure_def_id, kind),
-            ty::Predicate::ObjectSafe(trait_def_id) =>
-                ty::Predicate::ObjectSafe(trait_def_id),
-            ty::Predicate::ConstEvaluatable(def_id, substs) =>
-                ty::Predicate::ConstEvaluatable(def_id, substs.fold_with(folder)),
+            ty::Predicate::Poly(ref a) => ty::Predicate::Poly(a.fold_with(folder)),
+            ty::Predicate::Atom(ref a) => ty::Predicate::Atom(a.fold_with(folder)),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        match *self {
+            ty::Predicate::Poly(ref a) => a.visit_with(visitor),
+            ty::Predicate::Atom(ref a) => a.visit_with(visitor),
+        }
+    }
+}
+
+impl<'tcx> TypeFoldable<'tcx> for ty::PredicateAtom<'tcx> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        match *self {
+            ty::PredicateAtom::Trait(ref a) =>
+                ty::PredicateAtom::Trait(a.fold_with(folder)),
+            ty::PredicateAtom::Subtype(ref binder) =>
+                ty::PredicateAtom::Subtype(binder.fold_with(folder)),
+            ty::PredicateAtom::RegionOutlives(ref binder) =>
+                ty::PredicateAtom::RegionOutlives(binder.fold_with(folder)),
+            ty::PredicateAtom::TypeOutlives(ref binder) =>
+                ty::PredicateAtom::TypeOutlives(binder.fold_with(folder)),
+            ty::PredicateAtom::Projection(ref binder) =>
+                ty::PredicateAtom::Projection(binder.fold_with(folder)),
+            ty::PredicateAtom::WellFormed(data) =>
+                ty::PredicateAtom::WellFormed(data.fold_with(folder)),
+            ty::PredicateAtom::ClosureKind(closure_def_id, kind) =>
+                ty::PredicateAtom::ClosureKind(closure_def_id, kind),
+            ty::PredicateAtom::ObjectSafe(trait_def_id) =>
+                ty::PredicateAtom::ObjectSafe(trait_def_id),
+            ty::PredicateAtom::ConstEvaluatable(def_id, substs) =>
+                ty::PredicateAtom::ConstEvaluatable(def_id, substs.fold_with(folder)),
         }
     }
 
     fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
         match *self {
-            ty::Predicate::Trait(ref a) => a.visit_with(visitor),
-            ty::Predicate::Subtype(ref binder) => binder.visit_with(visitor),
-            ty::Predicate::RegionOutlives(ref binder) => binder.visit_with(visitor),
-            ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor),
-            ty::Predicate::Projection(ref binder) => binder.visit_with(visitor),
-            ty::Predicate::WellFormed(data) => data.visit_with(visitor),
-            ty::Predicate::ClosureKind(_closure_def_id, _kind) => false,
-            ty::Predicate::ObjectSafe(_trait_def_id) => false,
-            ty::Predicate::ConstEvaluatable(_def_id, substs) => substs.visit_with(visitor),
+            ty::PredicateAtom::Trait(ref a) => a.visit_with(visitor),
+            ty::PredicateAtom::Subtype(ref binder) => binder.visit_with(visitor),
+            ty::PredicateAtom::RegionOutlives(ref binder) => binder.visit_with(visitor),
+            ty::PredicateAtom::TypeOutlives(ref binder) => binder.visit_with(visitor),
+            ty::PredicateAtom::Projection(ref binder) => binder.visit_with(visitor),
+            ty::PredicateAtom::WellFormed(data) => data.visit_with(visitor),
+            ty::PredicateAtom::ClosureKind(_closure_def_id, _kind) => false,
+            ty::PredicateAtom::ObjectSafe(_trait_def_id) => false,
+            ty::PredicateAtom::ConstEvaluatable(_def_id, substs) => substs.visit_with(visitor),
         }
     }
 }
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 0b4d73b6ae56b..39e17df9d1166 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -15,7 +15,7 @@ use hir::def_id::DefId;
 use middle::const_val::ConstVal;
 use middle::region;
 use ty::subst::{Substs, Subst};
-use ty::{self, AdtDef, TypeFlags, Ty, TyCtxt, TypeFoldable};
+use ty::{self, AdtDef, TypeFlags, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use ty::{Slice, TyS};
 use ty::subst::Kind;
 
@@ -342,18 +342,16 @@ impl<'a, 'gcx, 'tcx> ExistentialPredicate<'tcx> {
 impl<'a, 'gcx, 'tcx> Binder<ExistentialPredicate<'tcx>> {
     pub fn with_self_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, self_ty: Ty<'tcx>)
         -> ty::Predicate<'tcx> {
-        use ty::ToPredicate;
         match *self.skip_binder() {
-            ExistentialPredicate::Trait(tr) => Binder(tr).with_self_ty(tcx, self_ty).to_predicate(),
+            ExistentialPredicate::Trait(tr) =>
+                Binder(tr).with_self_ty(tcx, self_ty).to_predicate(),
             ExistentialPredicate::Projection(p) =>
-                ty::Predicate::Projection(Binder(p.with_self_ty(tcx, self_ty))),
-            ExistentialPredicate::AutoTrait(did) => {
-                let trait_ref = Binder(ty::TraitRef {
+                Binder(p.with_self_ty(tcx, self_ty)).to_predicate(),
+            ExistentialPredicate::AutoTrait(did) =>
+                Binder(ty::TraitRef {
                     def_id: did,
                     substs: tcx.mk_substs_trait(self_ty, &[]),
-                });
-                trait_ref.to_predicate()
-            }
+                }).to_predicate(),
         }
     }
 }
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index c46192982c3fd..1ce1326399124 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -373,7 +373,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn required_region_bounds(self,
                                   erased_self_ty: Ty<'tcx>,
                                   predicates: Vec<ty::Predicate<'tcx>>)
-                                  -> Vec<ty::Region<'tcx>>    {
+                                  -> Vec<ty::Region<'tcx>>
+    {
         debug!("required_region_bounds(erased_self_ty={:?}, predicates={:?})",
                erased_self_ty,
                predicates);
@@ -382,18 +383,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 
         traits::elaborate_predicates(self, predicates)
             .filter_map(|predicate| {
-                match predicate {
-                    ty::Predicate::Projection(..) |
-                    ty::Predicate::Trait(..) |
-                    ty::Predicate::Subtype(..) |
-                    ty::Predicate::WellFormed(..) |
-                    ty::Predicate::ObjectSafe(..) |
-                    ty::Predicate::ClosureKind(..) |
-                    ty::Predicate::RegionOutlives(..) |
-                    ty::Predicate::ConstEvaluatable(..) => {
+                // We ignore late-bound regions below, so we can skip binder:
+                match predicate.skip_binders() {
+                    ty::PredicateAtom::Projection(..) |
+                    ty::PredicateAtom::Trait(..) |
+                    ty::PredicateAtom::Subtype(..) |
+                    ty::PredicateAtom::WellFormed(..) |
+                    ty::PredicateAtom::ObjectSafe(..) |
+                    ty::PredicateAtom::ClosureKind(..) |
+                    ty::PredicateAtom::ConstEvaluatable(..) |
+                    ty::PredicateAtom::RegionOutlives(..) => {
                         None
                     }
-                    ty::Predicate::TypeOutlives(ty::Binder(ty::OutlivesPredicate(t, r))) => {
+                    ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(t, r)) => {
                         // Search for a bound of the form `erased_self_ty
                         // : 'a`, but be wary of something like `for<'a>
                         // erased_self_ty : 'a` (we interpret a
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs
index 24e953bcd8ce0..635b3627efe0e 100644
--- a/src/librustc/ty/wf.rs
+++ b/src/librustc/ty/wf.rs
@@ -12,7 +12,7 @@ use hir::def_id::DefId;
 use middle::const_val::{ConstVal, ConstAggregate};
 use infer::InferCtxt;
 use ty::subst::Substs;
-use traits;
+use traits::{self, PredicateObligation};
 use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use std::iter::once;
 use syntax::ast;
@@ -54,7 +54,7 @@ pub fn obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
 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>,
+                                         trait_ref: ty::TraitRef<'tcx>,
                                          span: Span)
                                          -> Vec<traits::PredicateObligation<'tcx>>
 {
@@ -72,33 +72,30 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
 {
     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 {
-        ty::Predicate::Trait(ref t) => {
-            wf.compute_trait_ref(&t.skip_binder(), Elaborate::None); // (*)
+    // OK to skip binders, because wf code is prepared for it:
+    match predicate.skip_binders() {
+        ty::PredicateAtom::Trait(t) => {
+            wf.compute_trait_ref(t, Elaborate::None);
         }
-        ty::Predicate::RegionOutlives(..) => {
-        }
-        ty::Predicate::TypeOutlives(ref t) => {
-            wf.compute(t.skip_binder().0);
+        ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(t, _r)) => {
+            wf.compute(t);
         }
-        ty::Predicate::Projection(ref t) => {
-            let t = t.skip_binder(); // (*)
-            wf.compute_projection(t.projection_ty);
-            wf.compute(t.ty);
+        ty::PredicateAtom::Projection(ty::ProjectionPredicate { projection_ty, ty }) => {
+            wf.compute_projection(projection_ty);
+            wf.compute(ty);
         }
-        ty::Predicate::WellFormed(t) => {
+        ty::PredicateAtom::WellFormed(t) => {
             wf.compute(t);
         }
-        ty::Predicate::ObjectSafe(_) => {
+        ty::PredicateAtom::RegionOutlives(_) |
+        ty::PredicateAtom::ObjectSafe(_) |
+        ty::PredicateAtom::ClosureKind(_, _) => {
         }
-        ty::Predicate::ClosureKind(..) => {
+        ty::PredicateAtom::Subtype(ty::SubtypePredicate { a, b, .. }) => {
+            wf.compute(a);
+            wf.compute(b);
         }
-        ty::Predicate::Subtype(ref data) => {
-            wf.compute(data.skip_binder().a); // (*)
-            wf.compute(data.skip_binder().b); // (*)
-        }
-        ty::Predicate::ConstEvaluatable(def_id, substs) => {
+        ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
             let obligations = wf.nominal_obligations(def_id, substs);
             wf.out.extend(obligations);
 
@@ -169,7 +166,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
 
     /// Pushes the obligations required for `trait_ref` to be WF into
     /// `self.out`.
-    fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
+    fn compute_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>, elaborate: Elaborate) {
         let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
 
         let cause = self.cause(traits::MiscObligation);
@@ -191,9 +188,10 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
         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))));
+                            .map(|ty| traits::PredicateObligation::from(
+                                cause.clone(),
+                                param_env,
+                                ty::PredicateAtom::WellFormed(ty))));
     }
 
     /// Pushes the obligations required for `trait_ref::Item` to be WF
@@ -203,7 +201,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
         // WF and (b) the trait-ref holds.  (It may also be
         // normalizable and be WF that way.)
         let trait_ref = data.trait_ref(self.infcx.tcx);
-        self.compute_trait_ref(&trait_ref, Elaborate::None);
+        self.compute_trait_ref(trait_ref, Elaborate::None);
 
         if !data.has_escaping_regions() {
             let cause = self.cause(traits::ProjectionWf(data));
@@ -242,11 +240,11 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
                 let obligations = self.nominal_obligations(def_id, substs);
                 self.out.extend(obligations);
 
-                let predicate = ty::Predicate::ConstEvaluatable(def_id, substs);
+                let predicate = ty::PredicateAtom::ConstEvaluatable(def_id, substs);
                 let cause = self.cause(traits::MiscObligation);
-                self.out.push(traits::Obligation::new(cause,
-                                                      self.param_env,
-                                                      predicate));
+                self.out.push(PredicateObligation::from(cause,
+                                                        self.param_env,
+                                                        predicate));
             }
         }
     }
@@ -322,12 +320,10 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
                     if !r.has_escaping_regions() && !mt.ty.has_escaping_regions() {
                         let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
                         self.out.push(
-                            traits::Obligation::new(
+                            traits::PredicateObligation::from(
                                 cause,
                                 param_env,
-                                ty::Predicate::TypeOutlives(
-                                    ty::Binder(
-                                        ty::OutlivesPredicate(mt.ty, r)))));
+                                ty::OutlivesPredicate(mt.ty, r)));
                     }
                 }
 
@@ -367,10 +363,10 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
                     let component_traits =
                         data.auto_traits().chain(data.principal().map(|p| p.def_id()));
                     self.out.extend(
-                        component_traits.map(|did| traits::Obligation::new(
+                        component_traits.map(|did| traits::PredicateObligation::from(
                             cause.clone(),
                             param_env,
-                            ty::Predicate::ObjectSafe(did)
+                            ty::PredicateAtom::ObjectSafe(did)
                         ))
                     );
                 }
@@ -397,9 +393,10 @@ 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,
-                                                    self.param_env,
-                                                    ty::Predicate::WellFormed(ty)));
+                            traits::PredicateObligation::from(
+                                cause,
+                                self.param_env,
+                                ty::PredicateAtom::WellFormed(ty)));
                     } else {
                         // Yes, resolved, proceed with the
                         // result. Should never return false because
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 81b593d0a0a4f..aa33bee99b02b 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -97,6 +97,10 @@ macro_rules! define_print {
       $vars:tt { debug $dbg:block display $disp:block } ) => {
         gen_print_impl! { $generic $target, $vars yes $disp yes $dbg }
     };
+    ( $generic:tt $target:ty,
+      $vars:tt { debug-and-display $dbg:block } ) => {
+        gen_print_impl! { $generic $target, $vars yes $dbg yes $dbg }
+    };
     ( $generic:tt $target:ty,
       $vars:tt { debug $dbg:block } ) => {
         gen_print_impl! { $generic $target, $vars no {
@@ -911,6 +915,7 @@ define_print_multi! {
         // ('tcx) ty::Binder<ty::TraitRef<'tcx>> is intentionally omited
     ('tcx) ty::Binder<ty::TraitRefPrintWithColon<'tcx>>,
     ('tcx) ty::Binder<ty::TraitRefPrintWithoutSelf<'tcx>>,
+    ('tcx) ty::Binder<ty::PredicateAtom<'tcx>>,
     ('tcx) ty::Binder<ty::FnSig<'tcx>>,
     ('tcx) ty::Binder<ty::SubtypePredicate<'tcx>>,
     ('tcx) ty::Binder<ty::ProjectionPredicate<'tcx>>,
@@ -1059,7 +1064,7 @@ define_print! {
                         let mut is_sized = false;
                         write!(f, "impl")?;
                         for predicate in bounds.predicates {
-                            if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() {
+                            if let Some(trait_ref) = predicate.poly_trait(tcx) {
                                 // Don't print +Sized, but rather +?Sized if absent.
                                 if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() {
                                     is_sized = true;
@@ -1255,24 +1260,35 @@ define_print! {
 
 define_print! {
     ('tcx) ty::Predicate<'tcx>, (self, f, cx) {
+        debug-and-display {
+            match *self {
+                ty::Predicate::Poly(ref data) => data.print(f, cx),
+                ty::Predicate::Atom(ref data) => data.print(f, cx),
+            }
+        }
+    }
+}
+
+define_print! {
+    ('tcx) ty::PredicateAtom<'tcx>, (self, f, cx) {
         display {
             match *self {
-                ty::Predicate::Trait(ref data) => data.print_with_colon().print(f, cx),
-                ty::Predicate::Subtype(ref predicate) => predicate.print(f, cx),
-                ty::Predicate::RegionOutlives(ref predicate) => predicate.print(f, cx),
-                ty::Predicate::TypeOutlives(ref predicate) => predicate.print(f, cx),
-                ty::Predicate::Projection(ref predicate) => predicate.print(f, cx),
-                ty::Predicate::WellFormed(ty) => print!(f, cx, print(ty), write(" well-formed")),
-                ty::Predicate::ObjectSafe(trait_def_id) =>
+                ty::PredicateAtom::Trait(ref data) => data.print_with_colon().print(f, cx),
+                ty::PredicateAtom::Subtype(ref predicate) => predicate.print(f, cx),
+                ty::PredicateAtom::RegionOutlives(ref predicate) => predicate.print(f, cx),
+                ty::PredicateAtom::TypeOutlives(ref predicate) => predicate.print(f, cx),
+                ty::PredicateAtom::Projection(ref predicate) => predicate.print(f, cx),
+                ty::PredicateAtom::WellFormed(ty) => print!(f, cx, print(ty), write(" well-formed")),
+                ty::PredicateAtom::ObjectSafe(trait_def_id) =>
                     ty::tls::with(|tcx| {
                         write!(f, "the trait `{}` is object-safe", tcx.item_path_str(trait_def_id))
                     }),
-                ty::Predicate::ClosureKind(closure_def_id, kind) =>
+                ty::PredicateAtom::ClosureKind(closure_def_id, kind) =>
                     ty::tls::with(|tcx| {
                         write!(f, "the closure `{}` implements the trait `{}`",
                                tcx.item_path_str(closure_def_id), kind)
                     }),
-                ty::Predicate::ConstEvaluatable(def_id, substs) => {
+                ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
                     write!(f, "the constant `")?;
                     cx.parameterized(f, substs, def_id, &[])?;
                     write!(f, "` can be evaluated")
@@ -1281,19 +1297,19 @@ define_print! {
         }
         debug {
             match *self {
-                ty::Predicate::Trait(ref a) => a.print_with_colon().print(f, cx),
-                ty::Predicate::Subtype(ref pair) => pair.print(f, cx),
-                ty::Predicate::RegionOutlives(ref pair) => pair.print(f, cx),
-                ty::Predicate::TypeOutlives(ref pair) => pair.print(f, cx),
-                ty::Predicate::Projection(ref pair) => pair.print(f, cx),
-                ty::Predicate::WellFormed(ty) => ty.print(f, cx),
-                ty::Predicate::ObjectSafe(trait_def_id) => {
+                ty::PredicateAtom::Trait(ref a) => a.print_with_colon().print(f, cx),
+                ty::PredicateAtom::Subtype(ref pair) => pair.print(f, cx),
+                ty::PredicateAtom::RegionOutlives(ref pair) => pair.print(f, cx),
+                ty::PredicateAtom::TypeOutlives(ref pair) => pair.print(f, cx),
+                ty::PredicateAtom::Projection(ref pair) => pair.print(f, cx),
+                ty::PredicateAtom::WellFormed(ty) => ty.print(f, cx),
+                ty::PredicateAtom::ObjectSafe(trait_def_id) => {
                     write!(f, "ObjectSafe({:?})", trait_def_id)
                 }
-                ty::Predicate::ClosureKind(closure_def_id, kind) => {
+                ty::PredicateAtom::ClosureKind(closure_def_id, kind) => {
                     write!(f, "ClosureKind({:?}, {:?})", closure_def_id, kind)
                 }
-                ty::Predicate::ConstEvaluatable(def_id, substs) => {
+                ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
                     write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs)
                 }
             }
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index c628876a16fd8..e8045adad3415 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -398,15 +398,13 @@ impl<'b, 'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
         let predicates = self.ev.tcx.predicates_of(self.item_def_id);
         for predicate in &predicates.predicates {
             predicate.visit_with(self);
-            match predicate {
-                &ty::Predicate::Trait(poly_predicate) => {
-                    self.check_trait_ref(*poly_predicate.skip_binder());
+            match predicate.skip_binders() {
+                ty::PredicateAtom::Trait(trait_ref) => {
+                    self.check_trait_ref(trait_ref);
                 },
-                &ty::Predicate::Projection(poly_predicate) => {
+                ty::PredicateAtom::Projection(proj) => {
                     let tcx = self.ev.tcx;
-                    self.check_trait_ref(
-                        poly_predicate.skip_binder().projection_ty.trait_ref(tcx)
-                    );
+                    self.check_trait_ref(proj.projection_ty.trait_ref(tcx));
                 },
                 _ => (),
             };
@@ -864,18 +862,17 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
             }
             ty::TyAnon(def_id, ..) => {
                 for predicate in &self.tcx.predicates_of(def_id).predicates {
-                    let trait_ref = match *predicate {
-                        ty::Predicate::Trait(ref poly_trait_predicate) => {
-                            Some(*poly_trait_predicate.skip_binder())
+                    let trait_ref = match predicate.skip_binders() {
+                        ty::PredicateAtom::Trait(trait_ref) => {
+                            Some(trait_ref)
                         }
-                        ty::Predicate::Projection(ref poly_projection_predicate) => {
-                            if poly_projection_predicate.skip_binder().ty.visit_with(self) {
+                        ty::PredicateAtom::Projection(projection_predicate) => {
+                            if projection_predicate.ty.visit_with(self) {
                                 return true;
                             }
-                            Some(poly_projection_predicate.skip_binder()
-                                                          .projection_ty.trait_ref(self.tcx))
+                            Some(projection_predicate.projection_ty.trait_ref(self.tcx))
                         }
-                        ty::Predicate::TypeOutlives(..) => None,
+                        ty::PredicateAtom::TypeOutlives(..) => None,
                         _ => bug!("unexpected predicate: {:?}", predicate),
                     };
                     if let Some(trait_ref) = trait_ref {
@@ -1277,15 +1274,11 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
         let predicates = self.tcx.predicates_of(self.item_def_id);
         for predicate in &predicates.predicates {
             predicate.visit_with(self);
-            match predicate {
-                &ty::Predicate::Trait(poly_predicate) => {
-                    self.check_trait_ref(*poly_predicate.skip_binder());
-                },
-                &ty::Predicate::Projection(poly_predicate) => {
+            match predicate.skip_binders() {
+                ty::PredicateAtom::Trait(trait_ref) => self.check_trait_ref(trait_ref),
+                ty::PredicateAtom::Projection(predicate) => {
                     let tcx = self.tcx;
-                    self.check_trait_ref(
-                        poly_predicate.skip_binder().projection_ty.trait_ref(tcx)
-                    );
+                    self.check_trait_ref(predicate.projection_ty.trait_ref(tcx));
                 },
                 _ => (),
             };
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 82ba53c4e0377..40165f4eff37d 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -342,7 +342,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         let trait_ref = &ast_trait_ref.trait_ref;
         let trait_def_id = self.trait_def_id(trait_ref);
 
-        debug!("ast_path_to_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id);
+        debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id);
 
         self.prohibit_type_params(trait_ref.path.segments.split_last().unwrap().1);
 
@@ -360,8 +360,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
             predicate.ok() // ok to ignore Err() because ErrorReported (see above)
         }));
 
-        debug!("ast_path_to_poly_trait_ref({:?}, projections={:?}) -> {:?}",
-               trait_ref, poly_projections, poly_trait_ref);
+        debug!("instantiate_poly_trait_ref: trait_ref={:?}", trait_ref);
+        debug!("instantiate_poly_trait_ref: poly_projections={:?}", poly_projections);
+        debug!("instantiate_poly_trait_ref: poly_trait_ref={:?}", poly_trait_ref);
         poly_trait_ref
     }
 
@@ -546,6 +547,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
     {
         let tcx = self.tcx();
 
+        debug!("conv_object_ty_poly_trait_ref(trait_bounds={:?}, lifetime={:?})",
+               trait_bounds, lifetime);
+
         if trait_bounds.is_empty() {
             span_err!(tcx.sess, span, E0224,
                       "at least one non-builtin trait is required for an object type");
@@ -688,7 +692,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         let tcx = self.tcx();
 
         let bounds: Vec<_> = self.get_type_parameter_bounds(span, ty_param_def_id)
-            .predicates.into_iter().filter_map(|p| p.to_opt_poly_trait_ref()).collect();
+            .predicates.into_iter().filter_map(|p| p.poly_trait(tcx)).collect();
 
         // Check that there is exactly one way to find an associated type with the
         // correct name.
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index 8b47ad6639ca4..bbde15ef5e27d 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -99,9 +99,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             expr_def_id,
             |_, _| span_bug!(expr.span, "closure has region param"),
             |_, _| {
-                self.infcx
-                    .next_ty_var(ty::UniverseIndex::ROOT,
-                                 TypeVariableOrigin::TransformedUpvar(expr.span))
+                self.infcx.next_ty_var(
+                    ty::UniverseIndex::ROOT,
+                    TypeVariableOrigin::TransformedUpvar(expr.span),
+                )
             },
         );
         let closure_type = self.tcx.mk_closure(expr_def_id, substs);
@@ -165,7 +166,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     .projection_bounds()
                     .filter_map(|pb| {
                         let pb = pb.with_self_ty(self.tcx, self.tcx.types.err);
-                        self.deduce_sig_from_projection(&pb)
+                        self.deduce_sig_from_projection(pb)
                     })
                     .next();
                 let kind = object_type
@@ -186,20 +187,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let fulfillment_cx = self.fulfillment_cx.borrow();
         // Here `expected_ty` is known to be a type inference variable.
 
-        let expected_sig = fulfillment_cx.pending_closure_predicates()
+        let expected_sig = fulfillment_cx
+            .pending_closure_predicates()
             .iter()
             .filter_map(|predicate| {
-                debug!("deduce_expectations_from_obligations: predicate={:?}", predicate);
+                debug!(
+                    "deduce_expectations_from_obligations: predicate={:?}",
+                    predicate
+                );
 
-                match *predicate {
+                if let Some(poly_proj) = predicate.poly_projection(self.tcx) {
                     // Given a Projection predicate, we can potentially infer
                     // the complete signature.
-                    ty::Predicate::Projection(ref proj_predicate) => {
-                        let trait_ref = proj_predicate.to_poly_trait_ref(self.tcx);
-                        self.self_type_matches_expected_vid(trait_ref, expected_vid)
-                            .and_then(|_| self.deduce_sig_from_projection(proj_predicate))
-                    }
-                    _ => None,
+                    let poly_trait_ref = poly_proj.to_poly_trait_ref(self.tcx);
+                    self.self_type_matches_expected_vid(poly_trait_ref, expected_vid)
+                        .and_then(|_| self.deduce_sig_from_projection(poly_proj))
+                } else {
+                    None
                 }
             })
             .next();
@@ -208,29 +212,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // infer the kind. This can occur if there is a trait-reference
         // like `F : Fn<A>`. Note that due to subtyping we could encounter
         // many viable options, so pick the most restrictive.
-        let expected_kind = fulfillment_cx.pending_closure_predicates()
+        let expected_kind = fulfillment_cx
+            .pending_closure_predicates()
             .iter()
             .filter_map(|predicate| {
-                let opt_trait_ref = match *predicate {
-                    ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref(self.tcx)),
-                    ty::Predicate::Trait(data) => Some(data),
-                    ty::Predicate::Subtype(..) => None,
-                    ty::Predicate::RegionOutlives(..) => None,
-                    ty::Predicate::TypeOutlives(..) => None,
-                    ty::Predicate::WellFormed(..) => None,
-                    ty::Predicate::ObjectSafe(..) => None,
-                    ty::Predicate::ConstEvaluatable(..) => None,
-
-                    // NB: This predicate is created by breaking down a
-                    // `ClosureType: FnFoo()` predicate, where
-                    // `ClosureType` represents some `TyClosure`. It can't
-                    // possibly be referring to the current closure,
-                    // because we haven't produced the `TyClosure` for
-                    // this closure yet; this is exactly why the other
-                    // code is looking for a self type of a unresolved
-                    // inference variable.
-                    ty::Predicate::ClosureKind(..) => None,
-                };
+                // Look for *either* a projection predicate like `<T
+                // as Fn<()>>::Output = U` *or* a plain trait
+                // predicate like `T: Fn<()>` here:
+                let opt_trait_ref = predicate
+                    .poly_projection(self.tcx)
+                    .map(|proj| proj.to_poly_trait_ref(self.tcx))
+                    .or_else(|| predicate.poly_trait(self.tcx));
                 opt_trait_ref
                     .and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid))
                     .and_then(|tr| self.tcx.lang_items().fn_trait_kind(tr.def_id()))
@@ -242,11 +234,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         (expected_sig, expected_kind)
     }
 
-    /// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
-    /// everything we need to know about a closure.
+    /// Given a projection like "<F as Fn(X)>::Result == Y", we can
+    /// deduce everything we need to know about a closure.
     fn deduce_sig_from_projection(
         &self,
-        projection: &ty::PolyProjectionPredicate<'tcx>,
+        projection: ty::PolyProjectionPredicate<'tcx>,
     ) -> Option<ty::FnSig<'tcx>> {
         let tcx = self.tcx;
 
@@ -296,14 +288,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         trait_ref: ty::PolyTraitRef<'tcx>,
         expected_vid: ty::TyVid,
     ) -> Option<ty::PolyTraitRef<'tcx>> {
-        let self_ty = self.shallow_resolve(trait_ref.self_ty());
-        debug!(
-            "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
-            trait_ref,
-            self_ty
-        );
+        debug!("self_type_matches_expected_vid(trait_ref={:?})", trait_ref);
+
+        // OK to skip binder, we are looking for an unresolved type variable,
+        // which cannot be bound by this binder (since it is free).
+        let self_ty = trait_ref.skip_binder().self_ty();
+        if !self_ty.is_ty_var() {
+            return None;
+        }
+
+        let self_ty = self.shallow_resolve(self_ty);
+        debug!("self_type_matches_expected_vid: self_ty={:?}", self_ty);
         match self_ty.sty {
-            ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
+            ty::TyInfer(ty::TyVar(vid)) if vid == expected_vid => Some(trait_ref),
             _ => None,
         }
     }
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 12650558a6d85..8a5f314647db2 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -535,32 +535,40 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         let traits = [coerce_unsized_did, unsize_did];
         while let Some(obligation) = queue.pop_front() {
             debug!("coerce_unsized resolve step: {:?}", obligation);
-            let trait_ref = match obligation.predicate {
-                ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => {
+
+            // Continue only for predicates like `T: Unsize` where
+            // `Unsize` is one of the various unsize traits. Push
+            // everything else into `coercion.obligations`.
+            match obligation.predicate.poly_trait(self.tcx) {
+                Some(tr) if traits.contains(&tr.def_id()) => {
                     if unsize_did == tr.def_id() {
                         if let ty::TyTuple(..) = tr.0.input_types().nth(1).unwrap().sty {
                             debug!("coerce_unsized: found unsized tuple coercion");
                             has_unsized_tuple_coercion = true;
                         }
                     }
-                    tr.clone()
                 }
                 _ => {
                     coercion.obligations.push(obligation);
                     continue;
                 }
-            };
-            match selcx.select_poly(&obligation.with(trait_ref)) {
+            }
+
+            // Skolemize away the higher-ranked stuff and run `select`.
+            //
+            // (*) We know it's a trait obligation because of the match we just did.
+            let atom_obligation = self.skolemize_predicate_obligation(&obligation);
+            let trait_ref = atom_obligation.predicate.trait_().unwrap(); // see (*) above
+            match selcx.select(&atom_obligation.with(trait_ref)) {
                 // Uncertain or unimplemented.
-                Ok(None) |
-                Err(traits::Unimplemented) => {
+                Ok(None) | Err(traits::Unimplemented) => {
                     debug!("coerce_unsized: early return - can't prove obligation");
                     return Err(TypeError::Mismatch);
                 }
 
                 // Object safety violations or miscellaneous.
                 Err(err) => {
-                    self.report_selection_error(&obligation, &err);
+                    self.report_selection_error(&atom_obligation, &err);
                     // Treat this like an obligation and follow through
                     // with the unsizing - the lack of a coercion should
                     // be silent, as it causes a type mismatch later.
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 99b10efa01425..443d2cdf904a9 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -549,15 +549,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
         };
 
         traits::elaborate_predicates(self.tcx, predicates.predicates.clone())
-            .filter_map(|predicate| {
-                match predicate {
-                    ty::Predicate::Trait(trait_pred) if trait_pred.def_id() == sized_def_id =>
-                        Some(trait_pred),
-                    _ => None,
-                }
-            })
+            .filter_map(|p| p.poly_trait(self.tcx))
+            .filter(|p| p.def_id() == sized_def_id)
             .any(|trait_pred| {
-                match trait_pred.0.self_ty().sty {
+                match trait_pred.skip_binder().self_ty().sty {
                     ty::TyDynamic(..) => true,
                     _ => false,
                 }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 7a49c7b8694d0..80e884f47bb5d 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -15,7 +15,7 @@ use hir::def::Def;
 use hir::def_id::DefId;
 use namespace::Namespace;
 use rustc::ty::subst::Substs;
-use rustc::traits;
+use rustc::traits::{self, PredicateObligation};
 use rustc::ty::{self, Ty, ToPredicate, TraitRef, TypeFoldable};
 use rustc::ty::subst::Subst;
 use rustc::infer::{self, InferOk};
@@ -335,9 +335,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         debug!("lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
                method_ty,
                obligation);
-        obligations.push(traits::Obligation::new(cause,
-                                                 self.param_env,
-                                                 ty::Predicate::WellFormed(method_ty)));
+        obligations.push(
+            PredicateObligation::from(
+                cause,
+                self.param_env,
+                ty::PredicateAtom::WellFormed(method_ty)));
 
         let callee = MethodCallee {
             def_id,
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 2be213c0b065f..f5c31d589de80 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -588,24 +588,11 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
         let bounds: Vec<_> = self.param_env
             .caller_bounds
             .iter()
-            .filter_map(|predicate| {
-                match *predicate {
-                    ty::Predicate::Trait(trait_predicate) => {
-                        match trait_predicate.skip_binder().self_ty().sty {
-                            ty::TyParam(ref p) if *p == param_ty => {
-                                Some(trait_predicate)
-                            }
-                            _ => None,
-                        }
-                    }
-                    ty::Predicate::Subtype(..) |
-                    ty::Predicate::Projection(..) |
-                    ty::Predicate::RegionOutlives(..) |
-                    ty::Predicate::WellFormed(..) |
-                    ty::Predicate::ObjectSafe(..) |
-                    ty::Predicate::ClosureKind(..) |
-                    ty::Predicate::TypeOutlives(..) |
-                    ty::Predicate::ConstEvaluatable(..) => None,
+            .filter_map(|predicate| predicate.poly_trait(self.tcx))
+            .filter(|poly_trait_ref| {
+                match poly_trait_ref.skip_binder().self_ty().sty {
+                    ty::TyParam(p) => p == param_ty,
+                    _ => false,
                 }
             })
             .collect();
@@ -1070,8 +1057,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
                 let o = self.resolve_type_vars_if_possible(&o);
                 if !selcx.evaluate_obligation(&o) {
                     result = ProbeResult::NoMatch;
-                    if let &ty::Predicate::Trait(ref pred) = &o.predicate {
-                        possibly_unsatisfied_predicates.push(*pred.skip_binder());
+                    if let ty::PredicateAtom::Trait(pred) = o.predicate.skip_binders() {
+                        possibly_unsatisfied_predicates.push(pred);
                     }
                 }
             }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 0c44f9ebba2c2..ef886055e8246 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -93,7 +93,8 @@ use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
 use rustc::infer::type_variable::{TypeVariableOrigin};
 use rustc::middle::region;
 use rustc::ty::subst::{Subst, Substs};
-use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
+use rustc::traits::{self, FulfillmentContext, PredicateObligation,
+                    ObligationCause, ObligationCauseCode};
 use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
 use rustc::ty::{self, Ty, TyCtxt, Visibility};
 use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
@@ -1632,11 +1633,9 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> {
         ty::GenericPredicates {
             parent: None,
             predicates: self.param_env.caller_bounds.iter().filter(|predicate| {
-                match **predicate {
-                    ty::Predicate::Trait(ref data) => {
-                        data.0.self_ty().is_param(index)
-                    }
-                    _ => false
+                match predicate.skip_binders() {
+                    ty::PredicateAtom::Trait(ref data) => data.self_ty().is_param(index),
+                    _ => false,
                 }
             }).cloned().collect()
         }
@@ -2053,9 +2052,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,
-                                                        self.param_env,
-                                                        ty::Predicate::WellFormed(ty)));
+        self.register_predicate(PredicateObligation::from(cause,
+                                                          self.param_env,
+                                                          ty::PredicateAtom::WellFormed(ty)));
     }
 
     /// Registers obligations that all types appearing in `substs` are well-formed.
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 77dfc790338ab..8cc268a17004f 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -505,36 +505,37 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
                     .into_iter()
                     .flat_map(|obligation| {
                         assert!(!obligation.has_escaping_regions());
-                        match obligation.predicate {
-                            ty::Predicate::Trait(..) |
-                            ty::Predicate::Subtype(..) |
-                            ty::Predicate::Projection(..) |
-                            ty::Predicate::ClosureKind(..) |
-                            ty::Predicate::ObjectSafe(..) |
-                            ty::Predicate::ConstEvaluatable(..) =>
+                        match obligation.predicate.skip_binders() {
+                            ty::PredicateAtom::Trait(..) |
+                            ty::PredicateAtom::Subtype(..) |
+                            ty::PredicateAtom::Projection(..) |
+                            ty::PredicateAtom::ClosureKind(..) |
+                            ty::PredicateAtom::ObjectSafe(..) |
+                            ty::PredicateAtom::ConstEvaluatable(..) =>
                                 vec![],
 
-                            ty::Predicate::WellFormed(subty) => {
+                            ty::PredicateAtom::WellFormed(subty) => {
+                                assert!(!subty.has_escaping_regions());
                                 wf_types.push(subty);
                                 vec![]
                             }
 
-                            ty::Predicate::RegionOutlives(ref data) =>
-                                match self.tcx.no_late_bound_regions(data) {
-                                    None =>
-                                        vec![],
-                                    Some(ty::OutlivesPredicate(r_a, r_b)) =>
-                                        vec![ImpliedBound::RegionSubRegion(r_b, r_a)],
+                            ty::PredicateAtom::RegionOutlives(data) =>
+                                if data.has_escaping_regions() {
+                                    vec![]
+                                } else {
+                                    let ty::OutlivesPredicate(r_a, r_b) = data;
+                                    vec![ImpliedBound::RegionSubRegion(r_b, r_a)]
                                 },
 
-                            ty::Predicate::TypeOutlives(ref data) =>
-                                match self.tcx.no_late_bound_regions(data) {
-                                    None => vec![],
-                                    Some(ty::OutlivesPredicate(ty_a, r_b)) => {
-                                        let ty_a = self.resolve_type_vars_if_possible(&ty_a);
-                                        let components = self.tcx.outlives_components(ty_a);
-                                        self.implied_bounds_from_components(r_b, components)
-                                    }
+                            ty::PredicateAtom::TypeOutlives(data) =>
+                                if data.has_escaping_regions() {
+                                    vec![]
+                                } else {
+                                    let ty::OutlivesPredicate(ty_a, r_b) = data;
+                                    let ty_a = self.resolve_type_vars_if_possible(&ty_a);
+                                    let components = self.tcx.outlives_components(ty_a);
+                                    self.implied_bounds_from_components(r_b, components)
                                 },
                         }}));
         }
@@ -1810,9 +1811,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         traits::elaborate_predicates(self.tcx, predicates)
             .filter_map(|predicate| {
                 // we're only interesting in `T : 'a` style predicates:
-                let outlives = match predicate {
-                    ty::Predicate::TypeOutlives(data) => data,
-                    _ => { return None; }
+                let outlives = match predicate.poly_type_outlives(self.tcx) {
+                    Some(data) => data,
+                    None => return None,
                 };
 
                 debug!("projection_bounds: outlives={:?} (1)",
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 483af08cabfce..942c4f117309a 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -271,12 +271,11 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
         // traits.
         let has_predicates =
             predicates.predicates.iter().any(|predicate| {
-                match predicate {
-                    &ty::Predicate::Trait(ref poly_trait_ref) => {
-                        let self_ty = poly_trait_ref.0.self_ty();
-                        !(self_ty.is_self() && poly_trait_ref.def_id() == trait_def_id)
-                    },
-                    _ => true,
+                if let Some(poly_trait_ref) = predicate.poly_trait(self.tcx) {
+                    let self_ty = poly_trait_ref.skip_binder().self_ty();
+                    !(self_ty.is_self() && poly_trait_ref.def_id() == trait_def_id)
+                } else {
+                    true
                 }
             });
 
@@ -381,7 +380,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
                         ty::wf::trait_obligations(fcx,
                                                   fcx.param_env,
                                                   fcx.body_id,
-                                                  &trait_ref,
+                                                  trait_ref,
                                                   ast_trait_ref.path.span);
                     for obligation in obligations {
                         fcx.register_predicate(obligation);
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index c57c7c0b537bb..76d87348bf94c 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -696,7 +696,7 @@ fn super_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     // Now require that immediate supertraits are converted,
     // which will, in turn, reach indirect supertraits.
-    for bound in superbounds.iter().filter_map(|p| p.to_opt_poly_trait_ref()) {
+    for bound in superbounds.iter().filter_map(|p| p.poly_trait(tcx)) {
         tcx.at(item.span).super_predicates_of(bound.def_id());
     }
 
@@ -1471,7 +1471,7 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                                                        lifetime,
                                                                        None);
                             let pred = ty::Binder(ty::OutlivesPredicate(ty, region));
-                            predicates.push(ty::Predicate::TypeOutlives(pred))
+                            predicates.push(pred.to_predicate())
                         }
                     }
                 }
@@ -1482,7 +1482,7 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 for bound in &region_pred.bounds {
                     let r2 = AstConv::ast_region_to_region(&icx, bound, None);
                     let pred = ty::Binder(ty::OutlivesPredicate(r1, r2));
-                    predicates.push(ty::Predicate::RegionOutlives(pred))
+                    predicates.push(pred.to_predicate());
                 }
             }
 
@@ -1616,7 +1616,7 @@ fn predicates_from_bound<'tcx>(astconv: &AstConv<'tcx, 'tcx>,
         hir::RegionTyParamBound(ref lifetime) => {
             let region = astconv.ast_region_to_region(lifetime, None);
             let pred = ty::Binder(ty::OutlivesPredicate(param_ty, region));
-            vec![ty::Predicate::TypeOutlives(pred)]
+            vec![pred.to_predicate()]
         }
         hir::TraitTyParamBound(_, hir::TraitBoundModifier::Maybe) => {
             Vec::new()
diff --git a/src/librustc_typeck/constrained_type_params.rs b/src/librustc_typeck/constrained_type_params.rs
index 5f55b9b06ef1b..4ed718116fd6a 100644
--- a/src/librustc_typeck/constrained_type_params.rs
+++ b/src/librustc_typeck/constrained_type_params.rs
@@ -169,11 +169,9 @@ pub fn setup_constraining_predicates<'tcx>(tcx: TyCtxt,
         changed = false;
 
         for j in i..predicates.len() {
-            if let ty::Predicate::Projection(ref poly_projection) = predicates[j] {
-                // Note that we can skip binder here because the impl
-                // trait ref never contains any late-bound regions.
-                let projection = poly_projection.skip_binder();
-
+            // Note that we can skip binder here because the impl
+            // trait ref never contains any late-bound regions.
+            if let ty::PredicateAtom::Projection(projection) = predicates[j].skip_binders() {
                 // Special case: watch out for some kind of sneaky attempt
                 // to project out an associated type defined by this very
                 // trait.
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index dd36b28bb39ac..9565d0506df96 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -143,7 +143,8 @@ fn ty_bounds(bounds: Vec<clean::TyParamBound>) -> Vec<clean::TyParamBound> {
     bounds
 }
 
-fn trait_is_same_or_supertrait(cx: &DocContext, child: DefId,
+fn trait_is_same_or_supertrait(cx: &DocContext,
+                               child: DefId,
                                trait_: DefId) -> bool {
     if child == trait_ {
         return true

From 36120036176ebe46196f6e561df2bc3f24960caa Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Tue, 19 Sep 2017 08:29:19 -0400
Subject: [PATCH 49/63] encapsulate `select()` in a helper routine on
 `InferCtxt`

---
 src/librustc/traits/fulfill.rs                |  2 +-
 src/librustc/traits/mod.rs                    | 74 ++++++++++++++++++-
 src/librustc/traits/project.rs                |  2 +-
 src/librustc/traits/select.rs                 | 22 ++----
 src/librustc/traits/trans/mod.rs              | 12 ++-
 .../obligation_forest/mod.rs                  |  7 +-
 src/librustc_lint/builtin.rs                  |  9 +--
 src/librustc_typeck/check/coercion.rs         | 18 ++---
 src/librustc_typeck/check/method/probe.rs     |  6 +-
 9 files changed, 97 insertions(+), 55 deletions(-)

diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index a5a3754af3902..0f13f8209efe8 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -490,7 +490,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
                 Ok(Some(vtable)) => {
                     debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
                            trait_ref, obligation.recursion_depth);
-                    Ok(Some(vtable.nested_obligations()))
+                    Ok(Some(vtable.into_nested_obligations()))
                 }
                 Ok(None) => {
                     debug!("selecting trait `{:?}` at depth {} yielded Ok(None)",
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 587029e9a6c65..4a5677f4bca11 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -715,6 +715,65 @@ fn vtable_methods<'a, 'tcx>(
     )
 }
 
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    /// A kind of specialized routine that takes a given trait-ref and
+    /// tries to "select" a vtable that implements it. Can return in
+    /// one of three possible states:
+    ///
+    /// - Success: returns the vtable that we selected for this
+    ///   trait-ref. This vtable contains pending predicates that were not
+    ///   yet proven to hold. If this operation is not inside of a
+    ///   snapshot, or is inside a snapshot that is committed, those
+    ///   pending predicates should be registered in some enclosing
+    ///   fufillment cx. (If we *are* in a snapshot, and that snapshot
+    ///   is rolled-back, then the predicates can be discarded.)
+    ///
+    /// - Ambiguity: if `None` is returned, then the trait-ref could not
+    ///   be selected. Try again some other time.
+    ///
+    /// - Error: selecting the trait-ref led to an error. If this
+    ///   operation is inside of a snapshot, and that snapshot is
+    ///   rolled back, then these errors can be discarded. Otherwise,
+    ///   they should be reported to the user.
+    ///
+    /// NB. In the success/error case, unless this method is invoked
+    /// in a snapshot that is rolled back, you *must* process the
+    /// return value appropriately or it could lead to incorrect
+    /// compilations or ICEs. In particular, it could mess with the
+    /// projection cache, which assumes that subobligations will be
+    /// fully processed and errors will be reported.
+    pub fn select_trait_ref(&self,
+                            cause: ObligationCause<'tcx>,
+                            param_env: ty::ParamEnv<'tcx>,
+                            trait_ref: ty::TraitRef<'tcx>)
+                            -> Result<Option<Selection<'tcx>>, Vec<FulfillmentError<'tcx>>>
+    {
+        debug!("trait_ref(trait_ref={:?}, param_env={:?})", trait_ref, param_env);
+
+        let obligation = Obligation::new(cause, param_env, trait_ref);
+
+        let mut selcx = SelectionContext::new(self);
+        selcx.select(&obligation).map_err(|e| {
+            vec![FulfillmentError {
+                obligation: obligation.with(trait_ref.to_predicate_atom()),
+                code: FulfillmentErrorCode::CodeSelectionError(e),
+            }]
+        })
+    }
+
+    pub fn select_poly_trait_ref(&self,
+                                 cause: ObligationCause<'tcx>,
+                                 param_env: ty::ParamEnv<'tcx>,
+                                 poly_trait_ref: ty::PolyTraitRef<'tcx>)
+                                 -> Result<Option<Selection<'tcx>>, Vec<FulfillmentError<'tcx>>>
+    {
+        let (trait_ref, param_env, _skol_map) =
+            self.skolemize_late_bound_regions(param_env, &poly_trait_ref);
+
+        self.select_trait_ref(cause, param_env, trait_ref)
+    }
+}
+
 impl<'tcx> PredicateObligation<'tcx> {
     pub fn from<O>(cause: ObligationCause<'tcx>,
                    param_env: ty::ParamEnv<'tcx>,
@@ -783,7 +842,7 @@ impl<'tcx> ObligationCause<'tcx> {
 }
 
 impl<'tcx, N> Vtable<'tcx, N> {
-    pub fn nested_obligations(self) -> Vec<N> {
+    pub fn into_nested_obligations(self) -> Vec<N> {
         match self {
             VtableImpl(i) => i.nested,
             VtableParam(n) => n,
@@ -796,6 +855,19 @@ impl<'tcx, N> Vtable<'tcx, N> {
         }
     }
 
+    pub fn nested_obligations(&self) -> &[N] {
+        match *self {
+            VtableImpl(ref i) => &i.nested,
+            VtableParam(ref n) => &n,
+            VtableBuiltin(ref i) => &i.nested,
+            VtableDefaultImpl(ref d) => &d.nested,
+            VtableClosure(ref c) => &c.nested,
+            VtableGenerator(ref c) => &c.nested,
+            VtableObject(ref d) => &d.nested,
+            VtableFnPointer(ref d) => &d.nested,
+        }
+    }
+
     pub fn map<M, F>(self, f: F) -> Vtable<'tcx, M> where F: FnMut(N) -> M {
         match self {
             VtableImpl(i) => VtableImpl(VtableImplData {
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 917bad0ae0290..4d9c17feff84f 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -122,7 +122,7 @@ struct ProjectionTyCandidateSet<'tcx> {
 ///     <T as Trait>::U == V
 ///
 /// If successful, this may result in additional obligations.
-pub fn project_and_unify_type<'cx, 'gcx, 'tcx>(
+pub(in traits) fn project_and_unify_type<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionObligation<'tcx>)
     -> Result<Option<Vec<PredicateObligation<'tcx>>>,
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index ace96ed5f6ed3..955207febe074 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -17,7 +17,7 @@ use super::coherence;
 use super::DerivedObligationCause;
 use super::project;
 use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
-use super::{PredicateObligation, PredicateAtomObligation, PolyTraitObligation};
+use super::{PredicateObligation, PredicateAtomObligation};
 use super::{TraitObligation, ObligationCause};
 use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
 use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
@@ -474,21 +474,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
     /// Attempts to satisfy the obligation. If successful, this will affect the surrounding
     /// type environment by performing unification.
-    pub fn select_poly(&mut self, obligation: &PolyTraitObligation<'tcx>)
-                       -> SelectionResult<'tcx, Selection<'tcx>> {
-        debug!("select_poly({:?})", obligation);
-        assert!(!obligation.predicate.has_escaping_regions());
-        let (trait_ref, param_env, _skol_map) =
-            self.infcx().skolemize_late_bound_regions(obligation.param_env, &obligation.predicate);
-
-        let obligation = obligation.with(trait_ref).with_env(param_env);
-        self.select(&obligation)
-    }
-
-    /// Attempts to satisfy the obligation. If successful, this will affect the surrounding
-    /// type environment by performing unification.
-    pub fn select(&mut self, obligation: &TraitObligation<'tcx>)
-                  -> SelectionResult<'tcx, Selection<'tcx>> {
+    pub(in traits) fn select(&mut self, obligation: &TraitObligation<'tcx>)
+                             -> SelectionResult<'tcx, Selection<'tcx>>
+    {
         debug!("select({:?}, param_env={:?})", obligation, obligation.param_env);
 
         let tcx = self.tcx();
@@ -910,7 +898,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 Ok(selection) => {
                     this.evaluate_predicates_recursively(
                         stack.list(),
-                        selection.nested_obligations().iter())
+                        selection.nested_obligations())
                 }
                 Err(..) => EvaluatedToErr
             }
diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs
index 90afb16084dd6..aea24f06ac302 100644
--- a/src/librustc/traits/trans/mod.rs
+++ b/src/librustc/traits/trans/mod.rs
@@ -16,8 +16,10 @@
 use dep_graph::{DepKind, DepTrackingMapConfig};
 use infer::TransNormalize;
 use std::marker::PhantomData;
+use syntax::ast::DUMMY_NODE_ID;
 use syntax_pos::DUMMY_SP;
-use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, Vtable};
+use traits::{ObligationCause, FulfillmentContext, Vtable};
+
 use ty::{self, Ty, TyCtxt};
 use ty::subst::{Subst, Substs};
 use ty::fold::{TypeFoldable, TypeFolder};
@@ -42,12 +44,8 @@ pub fn trans_fulfill_obligation<'a, 'tcx>(ty: 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.
     ty.infer_ctxt().enter(|infcx| {
-        let mut selcx = SelectionContext::new(&infcx);
-
-        let obligation_cause = ObligationCause::dummy();
-        let obligation = Obligation::new(obligation_cause, param_env, trait_ref);
-
-        let selection = match selcx.select_poly(&obligation) {
+        let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID);
+        let selection = match infcx.select_poly_trait_ref(cause, param_env, trait_ref) {
             Ok(Some(selection)) => selection,
             Ok(None) => {
                 // Ambiguity can happen when monomorphizing during trans
diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs
index 02cae52166ac3..87e4e8dce4263 100644
--- a/src/librustc_data_structures/obligation_forest/mod.rs
+++ b/src/librustc_data_structures/obligation_forest/mod.rs
@@ -234,14 +234,11 @@ impl<O: ForestObligation> ObligationForest<O> {
     }
 
     /// Returns the set of obligations that are in a pending state.
-    pub fn pending_obligations(&self) -> Vec<O>
-        where O: Clone
-    {
+    pub fn pending_obligations<'a>(&'a self) -> impl Iterator<Item = &'a O> + 'a {
         self.nodes
             .iter()
             .filter(|n| n.state.get() == NodeState::Pending)
-            .map(|n| n.obligation.clone())
-            .collect()
+            .map(|n| &n.obligation)
     }
 
     /// Perform a pass through the obligation list. This must
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index dc2e2fd7646ac..2f002a23f1d12 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1025,16 +1025,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
                 // Attempt to select a concrete impl before checking.
                 ty::TraitContainer(trait_def_id) => {
                     let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, callee_substs);
-                    let trait_ref = trait_ref;
                     let span = tcx.hir.span(expr_id);
-                    let obligation =
-                        traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
-                                                cx.param_env,
-                                                trait_ref);
 
                     tcx.infer_ctxt().enter(|infcx| {
-                        let mut selcx = traits::SelectionContext::new(&infcx);
-                        match selcx.select(&obligation) {
+                        let cause = traits::ObligationCause::misc(span, expr_id);
+                        match infcx.select_trait_ref(cause, cx.param_env, trait_ref) {
                             // The method comes from a `T: Trait` bound.
                             // If `T` is `Self`, then this call is inside
                             // a default method definition.
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 8a5f314647db2..c0b34155125d1 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -513,8 +513,6 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             }
         })?;
 
-        let mut selcx = traits::SelectionContext::new(self);
-
         // Use a FIFO queue for this custom fulfillment procedure.
         let mut queue = VecDeque::new();
 
@@ -559,23 +557,17 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             // (*) We know it's a trait obligation because of the match we just did.
             let atom_obligation = self.skolemize_predicate_obligation(&obligation);
             let trait_ref = atom_obligation.predicate.trait_().unwrap(); // see (*) above
-            match selcx.select(&atom_obligation.with(trait_ref)) {
+            match self.select_trait_ref(atom_obligation.cause,
+                                        atom_obligation.param_env,
+                                        trait_ref) {
                 // Uncertain or unimplemented.
-                Ok(None) | Err(traits::Unimplemented) => {
+                Ok(None) | Err(_) => {
                     debug!("coerce_unsized: early return - can't prove obligation");
                     return Err(TypeError::Mismatch);
                 }
 
-                // Object safety violations or miscellaneous.
-                Err(err) => {
-                    self.report_selection_error(&atom_obligation, &err);
-                    // Treat this like an obligation and follow through
-                    // with the unsizing - the lack of a coercion should
-                    // be silent, as it causes a type mismatch later.
-                }
-
                 Ok(Some(vtable)) => {
-                    for obligation in vtable.nested_obligations() {
+                    for obligation in vtable.into_nested_obligations() {
                         queue.push_back(obligation);
                     }
                 }
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index f5c31d589de80..0683389d9a649 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -940,11 +940,11 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
     }
 
     fn select_trait_candidate(&self, trait_ref: ty::TraitRef<'tcx>)
-                              -> traits::SelectionResult<'tcx, traits::Selection<'tcx>>
+                              -> Result<Option<traits::Selection<'tcx>>,
+                                        Vec<traits::FulfillmentError<'tcx>>>
     {
         let cause = traits::ObligationCause::misc(self.span, self.body_id);
-        let obligation = traits::Obligation::new(cause, self.param_env, trait_ref);
-        traits::SelectionContext::new(self).select(&obligation)
+        self.select_trait_ref(cause, self.param_env, trait_ref)
     }
 
     fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>)

From 4548b3c22ada8ff5d1da410aad791c78fea3d164 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 4 Nov 2017 20:11:32 -0400
Subject: [PATCH 50/63] WIP fix print out of trait-ref

---
 src/librustc/traits/select.rs       |  2 +-
 src/librustc/ty/mod.rs              |  2 +-
 src/librustc/ty/structural_impls.rs | 14 +++++++++-----
 src/librustc/ty/sty.rs              | 21 ++++++++++++++++-----
 src/librustc/util/ppaux.rs          | 24 +++++++++---------------
 5 files changed, 36 insertions(+), 27 deletions(-)

diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 955207febe074..465c784e14e53 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -785,7 +785,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     let trait_ref = stack.obligation.predicate;
                     let self_ty = trait_ref.self_ty();
                     let cause = IntercrateAmbiguityCause::DownstreamCrate {
-                        trait_desc: trait_ref.print_with_colon().to_string(),
+                        trait_desc: trait_ref.print_without_self().to_string(),
                         self_desc: if self_ty.has_concrete_skeleton() {
                             Some(self_ty.to_string())
                         } else {
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index f8e129faa7238..ef3a017d369f6 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -63,7 +63,7 @@ pub use self::sty::{FnSig, GenSig, PolyFnSig, PolyGenSig};
 pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
 pub use self::sty::{ClosureSubsts, GeneratorInterior, TypeAndMut};
 pub use self::sty::{TraitRef, TypeVariants, PolyTraitRef};
-pub use self::sty::{TraitRefPrintWithoutSelf, TraitRefPrintWithColon};
+pub use self::sty::{TraitRefPrintWithoutSelf, TraitRefPrintWithSelf};
 pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef};
 pub use self::sty::{ExistentialProjection, PolyExistentialProjection, Const};
 pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region};
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 52561edeb2dd9..a351fbd61ec8c 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -106,10 +106,13 @@ impl<'a, 'tcx> Lift<'tcx> for ty::TraitRefPrintWithoutSelf<'a> {
     }
 }
 
-impl<'a, 'tcx> Lift<'tcx> for ty::TraitRefPrintWithColon<'a> {
-    type Lifted = ty::TraitRefPrintWithColon<'tcx>;
+impl<'a, 'tcx> Lift<'tcx> for ty::TraitRefPrintWithSelf<'a> {
+    type Lifted = ty::TraitRefPrintWithSelf<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
-        tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitRefPrintWithColon { trait_ref })
+        tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitRefPrintWithSelf {
+            trait_ref,
+            separator: self.separator,
+        })
     }
 }
 
@@ -793,10 +796,11 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TraitRefPrintWithoutSelf<'tcx> {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::TraitRefPrintWithColon<'tcx> {
+impl<'tcx> TypeFoldable<'tcx> for ty::TraitRefPrintWithSelf<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::TraitRefPrintWithColon {
+        ty::TraitRefPrintWithSelf {
             trait_ref: self.trait_ref.fold_with(folder),
+            separator: self.separator,
         }
     }
 
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 39e17df9d1166..aad1ad0c277fc 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -456,8 +456,14 @@ impl<'tcx> TraitRef<'tcx> {
 
     /// Returns a wrapper that implements `Display` and displays like
     /// `P0: Trait<..>`.
-    pub fn print_with_colon(self) -> TraitRefPrintWithColon<'tcx> {
-        TraitRefPrintWithColon { trait_ref: self }
+    pub fn print_with_colon(self) -> TraitRefPrintWithSelf<'tcx> {
+        TraitRefPrintWithSelf { trait_ref: self, separator: ": " }
+    }
+
+    /// Returns a wrapper that implements `Display` and displays like
+    /// `P0: Trait<..>`.
+    pub fn print_with_as(self) -> TraitRefPrintWithSelf<'tcx> {
+        TraitRefPrintWithSelf { trait_ref: self, separator: " as " }
     }
 
     /// Returns a wrapper that implements `Display` and displays like
@@ -470,8 +476,9 @@ impl<'tcx> TraitRef<'tcx> {
 /// Wrapper around TraitRef used only for its `Display` impl.
 /// This version displays like `P0: Trait<P1...Pn>`.
 #[derive(Copy, Clone)]
-pub struct TraitRefPrintWithColon<'tcx> {
-    pub trait_ref: TraitRef<'tcx>
+pub struct TraitRefPrintWithSelf<'tcx> {
+    pub trait_ref: TraitRef<'tcx>,
+    pub separator: &'static str,
 }
 
 /// Wrapper around TraitRef used only for its `Display` impl.
@@ -503,10 +510,14 @@ impl<'tcx> PolyTraitRef<'tcx> {
         self.skip_binder().input_types()
     }
 
-    pub fn print_with_colon(self) -> Binder<TraitRefPrintWithColon<'tcx>> {
+    pub fn print_with_colon(self) -> Binder<TraitRefPrintWithSelf<'tcx>> {
         self.map_bound(|t| t.print_with_colon())
     }
 
+    pub fn print_with_as(self) -> Binder<TraitRefPrintWithSelf<'tcx>> {
+        self.map_bound(|t| t.print_with_as())
+    }
+
     pub fn print_without_self(self) -> Binder<TraitRefPrintWithoutSelf<'tcx>> {
         self.map_bound(|t| t.print_without_self())
     }
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index aa33bee99b02b..8b6e512596b57 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -913,7 +913,7 @@ define_print_multi! {
     [
     ('tcx) ty::Binder<&'tcx ty::Slice<ty::ExistentialPredicate<'tcx>>>,
         // ('tcx) ty::Binder<ty::TraitRef<'tcx>> is intentionally omited
-    ('tcx) ty::Binder<ty::TraitRefPrintWithColon<'tcx>>,
+    ('tcx) ty::Binder<ty::TraitRefPrintWithSelf<'tcx>>,
     ('tcx) ty::Binder<ty::TraitRefPrintWithoutSelf<'tcx>>,
     ('tcx) ty::Binder<ty::PredicateAtom<'tcx>>,
     ('tcx) ty::Binder<ty::FnSig<'tcx>>,
@@ -947,27 +947,18 @@ define_print! {
 
 define_print! {
     ('tcx) ty::TraitRefPrintWithoutSelf<'tcx>, (self, f, cx) {
-        display {
-            cx.parameterized(f, self.trait_ref.substs, self.trait_ref.def_id, &[])
-        }
-        debug {
+        debug-and-display {
             cx.parameterized(f, self.trait_ref.substs, self.trait_ref.def_id, &[])
         }
     }
 }
 
 define_print! {
-    ('tcx) ty::TraitRefPrintWithColon<'tcx>, (self, f, cx) {
-        display {
-            print!(f, cx,
-                   print(self.trait_ref.self_ty()),
-                   write(": "),
-                   print(self.trait_ref.print_without_self()))
-        }
-        debug {
+    ('tcx) ty::TraitRefPrintWithSelf<'tcx>, (self, f, cx) {
+        debug-and-display {
             print!(f, cx,
                    print(self.trait_ref.self_ty()),
-                   write(": "),
+                   write("{}", self.separator),
                    print(self.trait_ref.print_without_self()))
         }
     }
@@ -1241,7 +1232,10 @@ define_print! {
             let (trait_ref, item_name) = ty::tls::with(|tcx|
                 (self.trait_ref(tcx), tcx.associated_item(self.item_def_id).name)
             );
-            print!(f, cx, print_debug(trait_ref), write("::{}", item_name))
+            print!(f, cx,
+                   write("<"),
+                   print(trait_ref.print_with_as()),
+                   write(">::{}", item_name))
         }
     }
 }

From 41ecff9a378cc5be1007943b8c56836f0c5b190c Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 4 Nov 2017 20:11:45 -0400
Subject: [PATCH 51/63] FIXME: hack up error messages some more

---
 src/test/compile-fail/hrtb-higher-ranker-supertraits.rs | 2 --
 src/test/compile-fail/issue-19538.rs                    | 2 +-
 src/test/compile-fail/issue-20692.rs                    | 6 ++----
 src/test/compile-fail/issue-38604.rs                    | 2 +-
 src/test/compile-fail/issue-39970.rs                    | 2 +-
 src/test/compile-fail/kindck-inherited-copy-bound.rs    | 2 +-
 src/test/compile-fail/range-1.rs                        | 1 +
 src/test/compile-fail/trait-object-safety.rs            | 2 +-
 src/test/compile-fail/trait-test-2.rs                   | 2 +-
 9 files changed, 9 insertions(+), 12 deletions(-)

diff --git a/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs b/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
index 6d63f0c15ac42..014d6c7729f77 100644
--- a/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
+++ b/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
@@ -27,7 +27,6 @@ fn want_foo_for_some_tcx<'x,F>(f: &'x F)
     want_foo_for_some_tcx(f);
     want_foo_for_any_tcx(f);
     //~^ ERROR mismatched types
-    //~| ERROR mismatched types
 }
 
 fn want_foo_for_any_tcx<F>(f: &F)
@@ -46,7 +45,6 @@ fn want_bar_for_some_ccx<'x,B>(b: &B)
     want_bar_for_some_ccx(b);
     want_bar_for_any_ccx(b);
     //~^ ERROR mismatched types
-    //~| ERROR mismatched types
 }
 
 fn want_bar_for_any_ccx<B>(b: &B)
diff --git a/src/test/compile-fail/issue-19538.rs b/src/test/compile-fail/issue-19538.rs
index a619050058262..c9dc96622a74e 100644
--- a/src/test/compile-fail/issue-19538.rs
+++ b/src/test/compile-fail/issue-19538.rs
@@ -26,5 +26,5 @@ fn main() {
     let mut thing = Thing;
     let test: &mut Bar = &mut thing;
     //~^ ERROR E0038
-    //~| ERROR E0038
+    //~| ERROR E0308
 }
diff --git a/src/test/compile-fail/issue-20692.rs b/src/test/compile-fail/issue-20692.rs
index 3e44053875552..b0b6ad3714b2b 100644
--- a/src/test/compile-fail/issue-20692.rs
+++ b/src/test/compile-fail/issue-20692.rs
@@ -12,10 +12,8 @@ trait Array: Sized {}
 
 fn f<T: Array>(x: &T) {
     let _ = x
-    //~^ ERROR `Array` cannot be made into an object
-    //~| NOTE the trait cannot require that `Self : Sized`
-    //~| NOTE requirements on the impl of `std::ops::CoerceUnsized<&Array>`
-    //~| NOTE the trait `Array` cannot be made into an object
+    //~^ ERROR non-primitive cast
+    //~| NOTE an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
     as
     &Array;
     //~^ ERROR `Array` cannot be made into an object
diff --git a/src/test/compile-fail/issue-38604.rs b/src/test/compile-fail/issue-38604.rs
index c1939a7707f39..4994e360f5624 100644
--- a/src/test/compile-fail/issue-38604.rs
+++ b/src/test/compile-fail/issue-38604.rs
@@ -22,5 +22,5 @@ impl Foo for () {
 
 fn main() {
     let _f: Box<Foo> = //~ ERROR `Foo` cannot be made into an object
-        Box::new(()); //~ ERROR `Foo` cannot be made into an object
+        Box::new(()); //~ ERROR mismatched types
 }
diff --git a/src/test/compile-fail/issue-39970.rs b/src/test/compile-fail/issue-39970.rs
index 65ea1baa4a126..37c7a02878761 100644
--- a/src/test/compile-fail/issue-39970.rs
+++ b/src/test/compile-fail/issue-39970.rs
@@ -27,5 +27,5 @@ impl Visit for () where
 
 fn main() {
     <() as Visit>::visit();
-    //~^ ERROR type mismatch resolving `for<'a> <() as Array<'a>>::Element == ()`
+    //~^ ERROR type mismatch resolving `<() as Array<'a>>::Element == ()`
 }
diff --git a/src/test/compile-fail/kindck-inherited-copy-bound.rs b/src/test/compile-fail/kindck-inherited-copy-bound.rs
index 0731fbaf01c43..c04e6d7838fb6 100644
--- a/src/test/compile-fail/kindck-inherited-copy-bound.rs
+++ b/src/test/compile-fail/kindck-inherited-copy-bound.rs
@@ -33,7 +33,7 @@ fn b() {
     let y = &x;
     let z = &x as &Foo;
     //~^ ERROR E0038
-    //~| ERROR E0038
+    //~| ERROR E0605
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs
index 58794e3b35d53..c2dc1dd6f807b 100644
--- a/src/test/compile-fail/range-1.rs
+++ b/src/test/compile-fail/range-1.rs
@@ -23,4 +23,5 @@ pub fn main() {
     let arr: &[_] = &[1, 2, 3];
     let range = *arr..;
     //~^ ERROR `[{integer}]: std::marker::Sized` is not satisfied
+    //~| ERROR the trait bound `[{integer}]: std::marker::Sized` is not satisfied in `std::ops::RangeFrom<[{integer}]>`
 }
diff --git a/src/test/compile-fail/trait-object-safety.rs b/src/test/compile-fail/trait-object-safety.rs
index baf239f5956d6..ce5c28aa07cdd 100644
--- a/src/test/compile-fail/trait-object-safety.rs
+++ b/src/test/compile-fail/trait-object-safety.rs
@@ -23,5 +23,5 @@ impl Tr for St {
 
 fn main() {
     let _: &Tr = &St; //~ ERROR E0038
-    //~^ ERROR E0038
+    //~^ ERROR E0308
 }
diff --git a/src/test/compile-fail/trait-test-2.rs b/src/test/compile-fail/trait-test-2.rs
index b08aab6da852a..51c7ee792ee15 100644
--- a/src/test/compile-fail/trait-test-2.rs
+++ b/src/test/compile-fail/trait-test-2.rs
@@ -19,6 +19,6 @@ fn main() {
     10.blah::<i32, i32>(); //~ ERROR expected at most 1 type parameter, found 2 type parameters
     (box 10 as Box<bar>).dup();
     //~^ ERROR E0038
-    //~| ERROR E0038
+    //~| ERROR E0605
     //~| ERROR E0277
 }

From afc7723e6f8e8f294bef529b0302a9371b075748 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sun, 5 Nov 2017 04:44:26 -0500
Subject: [PATCH 52/63] WIP restore obscure corner of error-reporting to
 previous behavior

doesn't seem like *good* behavior, but
---
 src/librustc/infer/error_reporting/mod.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 72b3f8837a98e..2d34c593792de 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -803,10 +803,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 self.expected_found_str(exp_found)
             }
             ValuePairs::TraitRefs(ref exp_found) => {
-                self.expected_found_str(&exp_found.map(|t| t.print_with_colon()))
+                self.expected_found_str(&exp_found.map(|t| t.print_without_self()))
             }
             ValuePairs::PolyTraitRefs(ref exp_found) => {
-                self.expected_found_str(&exp_found.map(|t| t.print_with_colon()))
+                self.expected_found_str(&exp_found.map(|t| t.print_without_self()))
             }
         }
     }

From 10882991b85407d0bd954ee253995a42b170579a Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 8 Nov 2017 14:32:20 -0500
Subject: [PATCH 53/63] WIP VtableAuto in Vtable::nested_obligations

---
 src/librustc/traits/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 4a5677f4bca11..eb83816f7524e 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -860,7 +860,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
             VtableImpl(ref i) => &i.nested,
             VtableParam(ref n) => &n,
             VtableBuiltin(ref i) => &i.nested,
-            VtableDefaultImpl(ref d) => &d.nested,
+            VtableAutoImpl(ref d) => &d.nested,
             VtableClosure(ref c) => &c.nested,
             VtableGenerator(ref c) => &c.nested,
             VtableObject(ref d) => &d.nested,

From 161ce520af2d7203ab3c93880f59f24c643fa816 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 8 Nov 2017 14:32:34 -0500
Subject: [PATCH 54/63] WIP VtableAuto in `confirm_auto_impl_candidate`

---
 src/librustc/traits/select.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 465c784e14e53..f4cc313c75ed1 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -2292,7 +2292,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn confirm_auto_impl_candidate(&mut self,
                                    obligation: &TraitObligation<'tcx>,
                                    trait_def_id: DefId)
-                                   -> VtableDefaultImplData<PredicateObligation<'tcx>>
+                                   -> VtableAutoImplData<PredicateObligation<'tcx>>
     {
         debug!("confirm_auto_impl_candidate({:?}, {:?})",
                obligation,

From 7ea14fa5a906c85ac264e79358b1045203273693 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 8 Nov 2017 14:32:43 -0500
Subject: [PATCH 55/63] WIP VtableAuto in `vtable_auto_impl`

---
 src/librustc/traits/select.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index f4cc313c75ed1..0a67661e08aed 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -2308,7 +2308,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         obligation: &TraitObligation<'tcx>,
                         trait_def_id: DefId,
                         nested: Vec<Ty<'tcx>>)
-                        -> VtableDefaultImplData<PredicateObligation<'tcx>>
+                        -> VtableAutoImplData<PredicateObligation<'tcx>>
     {
         debug!("vtable_auto_impl: nested={:?}", nested);
 

From a84d26ea7e093ea922b04d63ffde38b25b024abe Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 8 Nov 2017 14:46:01 -0500
Subject: [PATCH 56/63] WIP privacy wrap lines after adding `print_without_self

---
 src/librustc_privacy/lib.rs | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index e8045adad3415..9ffc80d8e258c 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -877,7 +877,8 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
                     };
                     if let Some(trait_ref) = trait_ref {
                         if !self.item_is_accessible(trait_ref.def_id) {
-                            let msg = format!("trait `{}` is private", trait_ref.print_without_self());
+                            let msg = format!("trait `{}` is private",
+                                              trait_ref.print_without_self());
                             self.tcx.sess.span_err(self.span, &msg);
                             return true;
                         }
@@ -1316,7 +1317,8 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
             if !vis.is_at_least(self.required_visibility, self.tcx) {
                 if self.has_pub_restricted || self.has_old_errors {
                     struct_span_err!(self.tcx.sess, self.span, E0445,
-                                     "private trait `{}` in public interface", trait_ref.print_without_self())
+                                     "private trait `{}` in public interface",
+                                     trait_ref.print_without_self())
                         .span_label(self.span, format!(
                                     "private trait can't be public"))
                         .emit();
@@ -1325,7 +1327,8 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
                                        node_id,
                                        self.span,
                                        &format!("private trait `{}` in public \
-                                                 interface (error E0445)", trait_ref.print_without_self()));
+                                                 interface (error E0445)",
+                                                trait_ref.print_without_self()));
                 }
             }
         }

From 723d6f70abc2a98937cc38e93a519f6458f95eed Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 8 Nov 2017 14:46:13 -0500
Subject: [PATCH 57/63] WIP ppaux wrap lines after PredicateAtom

---
 src/librustc/util/ppaux.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 8b6e512596b57..9edd2cb5ca183 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -1272,7 +1272,9 @@ define_print! {
                 ty::PredicateAtom::RegionOutlives(ref predicate) => predicate.print(f, cx),
                 ty::PredicateAtom::TypeOutlives(ref predicate) => predicate.print(f, cx),
                 ty::PredicateAtom::Projection(ref predicate) => predicate.print(f, cx),
-                ty::PredicateAtom::WellFormed(ty) => print!(f, cx, print(ty), write(" well-formed")),
+                ty::PredicateAtom::WellFormed(ty) => {
+                    print!(f, cx, print(ty), write(" well-formed"))
+                }
                 ty::PredicateAtom::ObjectSafe(trait_def_id) =>
                     ty::tls::with(|tcx| {
                         write!(f, "the trait `{}` is object-safe", tcx.item_path_str(trait_def_id))

From cc9c5ec28c7643bc5cafc68106ba5403767e17bf Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 8 Nov 2017 14:46:27 -0500
Subject: [PATCH 58/63] WIP issue-20692.rs wrap lines

---
 src/test/compile-fail/issue-20692.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/compile-fail/issue-20692.rs b/src/test/compile-fail/issue-20692.rs
index b0b6ad3714b2b..ef204f61eb247 100644
--- a/src/test/compile-fail/issue-20692.rs
+++ b/src/test/compile-fail/issue-20692.rs
@@ -13,7 +13,7 @@ trait Array: Sized {}
 fn f<T: Array>(x: &T) {
     let _ = x
     //~^ ERROR non-primitive cast
-    //~| NOTE an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
+    //~| NOTE an `as` expression can only be used to convert between primitive types.
     as
     &Array;
     //~^ ERROR `Array` cannot be made into an object

From b7613c7834bb9acee11abbfbdb7c4de5edddb4be Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 8 Nov 2017 14:46:40 -0500
Subject: [PATCH 59/63] WIP range-1 wrap lines

---
 src/test/compile-fail/range-1.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs
index c2dc1dd6f807b..e8413b6dc61dc 100644
--- a/src/test/compile-fail/range-1.rs
+++ b/src/test/compile-fail/range-1.rs
@@ -23,5 +23,5 @@ pub fn main() {
     let arr: &[_] = &[1, 2, 3];
     let range = *arr..;
     //~^ ERROR `[{integer}]: std::marker::Sized` is not satisfied
-    //~| ERROR the trait bound `[{integer}]: std::marker::Sized` is not satisfied in `std::ops::RangeFrom<[{integer}]>`
+    //~| ERROR the trait bound `[{integer}]: std::marker::Sized` is not satisfied
 }

From 130ea18074da7b1e99d02cec5dd9fad6b195ec18 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 9 Nov 2017 04:17:09 -0500
Subject: [PATCH 60/63] WIP more work on tests

---
 .../regions-fn-subtyping-return-static.rs             |  5 +++--
 .../method-help-unsatisfied-bound.stderr              |  2 +-
 src/test/ui/regions-fn-subtyping-return-static.stderr | 11 -----------
 3 files changed, 4 insertions(+), 14 deletions(-)
 rename src/test/{ui => run-pass}/regions-fn-subtyping-return-static.rs (91%)
 delete mode 100644 src/test/ui/regions-fn-subtyping-return-static.stderr

diff --git a/src/test/ui/regions-fn-subtyping-return-static.rs b/src/test/run-pass/regions-fn-subtyping-return-static.rs
similarity index 91%
rename from src/test/ui/regions-fn-subtyping-return-static.rs
rename to src/test/run-pass/regions-fn-subtyping-return-static.rs
index 9098511186724..d024b539d85f6 100644
--- a/src/test/ui/regions-fn-subtyping-return-static.rs
+++ b/src/test/run-pass/regions-fn-subtyping-return-static.rs
@@ -47,8 +47,9 @@ fn baz(x: &S) -> &S {
 fn supply_F() {
     want_F(foo);
 
-    // FIXME(#33684) -- this should be a subtype, but current alg. rejects it incorrectly
-    want_F(bar); //~ ERROR E0308
+    // Note -- older algorithm rejected this case, but it is a valid
+    // subtyping.  See #33684.
+    want_F(bar);
 
     want_F(baz);
 }
diff --git a/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr b/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr
index ab5b3e1791528..187cf3ced9150 100644
--- a/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr
+++ b/src/test/ui/mismatched_types/method-help-unsatisfied-bound.stderr
@@ -5,7 +5,7 @@ error[E0599]: no method named `unwrap` found for type `std::result::Result<(), F
    |       ^^^^^^
    |
    = note: the method `unwrap` exists but the following trait bounds were not satisfied:
-           `Foo : std::fmt::Debug`
+           `Foo: std::fmt::Debug`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions-fn-subtyping-return-static.stderr b/src/test/ui/regions-fn-subtyping-return-static.stderr
deleted file mode 100644
index 4a97537223cf6..0000000000000
--- a/src/test/ui/regions-fn-subtyping-return-static.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/regions-fn-subtyping-return-static.rs:51:12
-   |
-51 |     want_F(bar); //~ ERROR E0308
-   |            ^^^ expected concrete lifetime, found bound lifetime parameter 'cx
-   |
-   = note: expected type `for<'cx> fn(&'cx S) -> &'cx S`
-              found type `for<'a> fn(&'a S) -> &S {bar::<'_>}`
-
-error: aborting due to previous error
-

From 328d3416de28f52bf5074e15e06af4df717fe479 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 9 Nov 2017 04:17:16 -0500
Subject: [PATCH 61/63] WIP make method suggestions print as they ever did

---
 src/librustc_typeck/check/method/suggest.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index ae2f9673ab027..c00962862aeff 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -271,7 +271,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
                 if !unsatisfied_predicates.is_empty() {
                     let bound_list = unsatisfied_predicates.iter()
-                        .map(|p| p.print_with_colon().to_string())
+                        .map(|p| format!("`{} : {}`", p.self_ty(), p.print_without_self()))
                         .collect::<Vec<_>>()
                         .join("\n");
                     err.note(&format!("the method `{}` exists but the following trait bounds \

From 9ff9ad593d0cdd1da39331bc834bc6c93c3bb3b9 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Wed, 8 Nov 2017 17:13:40 -0500
Subject: [PATCH 62/63] type and query canonicalizer

---
 src/librustc/ich/impls_ty.rs                  |   4 +
 src/librustc/infer/combine.rs                 |   2 +
 src/librustc/infer/error_reporting/mod.rs     |   2 +
 src/librustc/infer/freshen.rs                 |   7 +
 src/librustc/infer/region_inference/mod.rs    |   4 +-
 src/librustc/lib.rs                           |   1 +
 src/librustc/traits/mod.rs                    |   1 +
 .../traits/recursive_solver/canonical.rs      | 292 ++++++++++++++++++
 src/librustc/traits/recursive_solver/mod.rs   |  13 +
 src/librustc/traits/select.rs                 |  15 +-
 src/librustc/ty/error.rs                      |   1 +
 src/librustc/ty/mod.rs                        |   2 +-
 src/librustc/ty/sty.rs                        |   9 +
 src/librustc/ty/util.rs                       |   1 +
 src/librustc/util/ppaux.rs                    |   9 +
 .../borrowck/gather_loans/mod.rs              |   1 +
 src/librustc_typeck/check/writeback.rs        |   1 +
 src/librustc_typeck/variance/constraints.rs   |   1 +
 18 files changed, 358 insertions(+), 8 deletions(-)
 create mode 100644 src/librustc/traits/recursive_solver/canonical.rs
 create mode 100644 src/librustc/traits/recursive_solver/mod.rs

diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 20a45387f115b..45f98f275c099 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -12,6 +12,7 @@
 //! from rustc::ty in no particular order.
 
 use ich::{StableHashingContext, NodeIdHashingMode};
+use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
                                            StableHasher, StableHasherResult};
 use std::hash as std_hash;
@@ -75,6 +76,9 @@ for ty::RegionKind {
             ty::ReFree(ref free_region) => {
                 free_region.hash_stable(hcx, hasher);
             }
+            ty::ReCanonical(cvar) => {
+                cvar.index().hash_stable(hcx, hasher);
+            }
             ty::ReLateBound(..) |
             ty::ReVar(..) |
             ty::ReSkolemized(..) => {
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 48b0ccc747411..8dec4a2cb7738 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -483,6 +483,8 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                     ty::Bivariant | ty::Covariant | ty::Contravariant => (),
                 }
             }
+
+            ty::ReCanonical(..) => bug!("encountered canonical region during inference"),
         }
 
         // FIXME: This is non-ideal because we don't give a
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 2d34c593792de..75210abcc0e9a 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -246,6 +246,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             ty::ReErased => {
                 (format!("lifetime {:?}", region), None)
             }
+
+            ty::ReCanonical(..) => bug!("encountered canonical region during inference"),
         };
         let message = format!("{}{}{}", prefix, description, suffix);
         if let Some(span) = span {
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index 468bfb514262a..a5b486027c659 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -199,6 +199,10 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
                 // replace all free regions with 'erased
                 self.tcx().types.re_erased
             }
+
+            ty::ReCanonical(..) => {
+                bug!("encountered canonical region during type-freshening")
+            }
         }
     }
 
@@ -249,6 +253,9 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
                 t
             }
 
+            ty::TyInfer(ty::CanonicalTy(..)) =>
+                bug!("encountered canonical ty during freshening"),
+
             ty::TyClosure(def_id, substs) => {
                 self.freshen_closure_like(
                     def_id, substs, t,
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 1d4a1e2342f7d..9d524947a346c 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -24,7 +24,7 @@ use rustc_data_structures::unify as ut;
 use middle::free_region::RegionRelations;
 use ty::{self, Ty, TyCtxt};
 use ty::{Region, RegionVid};
-use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased};
+use ty::{ReCanonical, ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased};
 use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
 
 use std::cell::{Cell, RefCell};
@@ -697,6 +697,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                             b: Region<'tcx>)
                             -> Region<'tcx> {
         match (a, b) {
+            (&ReCanonical(..), _) |
+            (_, &ReCanonical(..)) |
             (&ReLateBound(..), _) |
             (_, &ReLateBound(..)) |
             (&ReErased, _) |
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 498e1aa3520d5..965905add20c3 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -49,6 +49,7 @@
 #![feature(inclusive_range_syntax)]
 #![cfg_attr(windows, feature(libc))]
 #![feature(macro_vis_matcher)]
+#![feature(match_default_bindings)]
 #![feature(never_type)]
 #![feature(nonzero)]
 #![feature(quote)]
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index eb83816f7524e..9b7c6069a544b 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -54,6 +54,7 @@ mod fulfill;
 mod project;
 mod object_safety;
 mod on_unimplemented;
+mod recursive_solver;
 mod select;
 mod specialize;
 mod structural_impls;
diff --git a/src/librustc/traits/recursive_solver/canonical.rs b/src/librustc/traits/recursive_solver/canonical.rs
new file mode 100644
index 0000000000000..376cf63c2ff83
--- /dev/null
+++ b/src/librustc/traits/recursive_solver/canonical.rs
@@ -0,0 +1,292 @@
+// Copyright 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.
+
+use infer::InferCtxt;
+use hir::def_id::DefId;
+use ty::{self, CanonicalVar, Ty, TyCtxt};
+use ty::fold::{TypeFoldable, TypeFolder};
+use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_data_structures::fx::FxHashMap;
+
+/// A "canonicalized" type `V` is one where all free inference
+/// variables have been rewriten to "canonical vars". These are
+/// numbered starting from 0 in order of first appearance.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Canonical<V> {
+    pub variables: IndexVec<CanonicalVar, CanonicalVarInfo>,
+    pub value: V,
+}
+
+/// When you canonicalize a value `V`, you get back a `Canonical<V>`
+/// formed by replacing unbound inference variables and things with
+/// `CanonicalVar`. This struct maps those canonical variables back to
+/// the things that were removed; with it, you can reconstruct the
+/// original `V`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct CanonicalVarValues<'tcx> {
+    pub var_values: IndexVec<CanonicalVar, CanonicalVarValue<'tcx>>,
+}
+
+/// After you canonicalize, we also return some extra information
+/// about closures and generators, for example what `Fn` traits a
+/// closure implements (if it has been inferred thus far). This
+/// information is needed by the trait checker and becomes part of the
+/// query environment. They are effectively "extra facts".
+pub struct CanonicalClosureFacts<'tcx> {
+    pub closure_kinds: FxHashMap<DefId, ty::ClosureKind>,
+    pub generator_sigs: FxHashMap<DefId, ty::PolyGenSig<'tcx>>,
+}
+
+/// Information about a canonical variable that is included with the
+/// canonical value. This is sufficient information for code to create
+/// a copy of the canonical value in some other inference context,
+/// with fresh inference variables replacing the canonical values.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct CanonicalVarInfo {
+    pub kind: CanonicalVarKind,
+    pub universe: ty::UniverseIndex,
+}
+
+/// Describes the "kind" of the canonical variable. This is a "kind"
+/// in the type-theory sense of the term -- i.e., a "meta" type system
+/// that analyzes type-like values.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum CanonicalVarKind {
+    /// Some kind of type inference variable.
+    Ty(CanonicalTyVarKind),
+
+    /// Region variable `'?R`.
+    Region,
+}
+
+/// Rust actually has more than one category of type variables;
+/// notably, the type variables we create for literals (e.g., 22 or
+/// 22.) can only be instantiated with integral/float types (e.g.,
+/// usize or f32). In order to faithfully reproduce a type, we need to
+/// know what set of types a given type variable can be unified with.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum CanonicalTyVarKind {
+    /// General type variable `?T` that can be unified with arbitrary types.
+    General,
+
+    /// Integral type variable `?I` (that can only be unified with integral types).
+    Int,
+
+    /// Floating-point type variable `?F` (that can only be unified with float types).
+    Float,
+}
+
+/// The value for a canonical variable.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum CanonicalVarValue<'tcx> {
+    /// Value from a Type/Int/Float type-variable `?T`.
+    Ty(Ty<'tcx>),
+
+    /// Region variable `'?R`.
+    Region(ty::Region<'tcx>),
+}
+
+impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
+    pub fn canonicalize<V>(&self, value: &V) -> (Canonical<V>,
+                                                 CanonicalClosureFacts<'tcx>,
+                                                 CanonicalVarValues<'tcx>)
+    where
+        V: TypeFoldable<'tcx>,
+    {
+        let mut canonicalizer = Canonicalizer {
+            infcx: self,
+            variables: IndexVec::default(),
+            indices: FxHashMap::default(),
+            var_values: IndexVec::default(),
+            closure_kinds: FxHashMap::default(),
+            generator_sigs: FxHashMap::default(),
+        };
+        let out_value = value.fold_with(&mut canonicalizer);
+        let canonical_value = Canonical {
+            variables: canonicalizer.variables,
+            value: out_value,
+        };
+        let canonical_closure_facts = CanonicalClosureFacts {
+            closure_kinds: canonicalizer.closure_kinds,
+            generator_sigs: canonicalizer.generator_sigs,
+        };
+        let canonical_var_values = CanonicalVarValues {
+            var_values: canonicalizer.var_values,
+        };
+        (canonical_value, canonical_closure_facts, canonical_var_values)
+    }
+}
+
+struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    variables: IndexVec<CanonicalVar, CanonicalVarInfo>,
+    indices: FxHashMap<CanonicalVarValue<'tcx>, CanonicalVar>,
+    var_values: IndexVec<CanonicalVar, CanonicalVarValue<'tcx>>,
+    closure_kinds: FxHashMap<DefId, ty::ClosureKind>,
+    generator_sigs: FxHashMap<DefId, ty::PolyGenSig<'tcx>>,
+}
+
+impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReLateBound(..) => {
+                // leave bound regions alone
+                r
+            }
+
+            ty::ReStatic |
+            ty::ReEarlyBound(..) |
+            ty::ReFree(_) |
+            ty::ReScope(_) |
+            ty::ReVar(_) |
+            ty::ReSkolemized(..) |
+            ty::ReEmpty |
+            ty::ReErased => {
+                // replace all free regions with 'erased
+                let info = CanonicalVarInfo {
+                    kind: CanonicalVarKind::Region,
+                    universe: ty::UniverseIndex::ROOT, // TODO
+                };
+                let cvar = self.canonical_var(info, CanonicalVarValue::Region(r));
+                self.tcx().mk_region(ty::ReCanonical(cvar))
+            }
+        }
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        if !t.needs_infer() && !t.has_erasable_regions()
+            && !(t.has_closure_types() && self.infcx.in_progress_tables.is_some())
+        {
+            return t;
+        }
+
+        let tcx = self.infcx.tcx;
+
+        match t.sty {
+            ty::TyInfer(ty::TyVar(v)) => {
+                self.canonicalize_ty_var(
+                    CanonicalTyVarKind::General,
+                    ty::UniverseIndex::ROOT, // TODO
+                    t,
+                )
+            }
+
+            ty::TyInfer(ty::IntVar(v)) => {
+                self.canonicalize_ty_var(
+                    CanonicalTyVarKind::Int,
+                    ty::UniverseIndex::ROOT, // TODO
+                    t,
+                )
+            }
+
+            ty::TyInfer(ty::FloatVar(v)) => {
+                self.canonicalize_ty_var(
+                    CanonicalTyVarKind::Float,
+                    ty::UniverseIndex::ROOT, // TODO
+                    t,
+                )
+            }
+
+            ty::TyInfer(ty::FreshTy(_)) |
+            ty::TyInfer(ty::FreshIntTy(_)) |
+            ty::TyInfer(ty::FreshFloatTy(_)) => {
+                bug!("encountered a fresh type during canonicalization")
+            }
+
+            ty::TyInfer(ty::CanonicalTy(_)) => {
+                bug!("encountered a canonical type during canonicalization")
+            }
+
+            ty::TyClosure(def_id, ..) => {
+                if let Some(kind) = self.infcx.closure_kind(def_id) {
+                    self.closure_kinds.insert(def_id, kind);
+                }
+                t.super_fold_with(self)
+            }
+
+            ty::TyGenerator(def_id, ..) => {
+                if let Some(gen_sig) = self.infcx.generator_sig(def_id) {
+                    self.generator_sigs.insert(def_id, gen_sig);
+                }
+                t.super_fold_with(self)
+            }
+
+            ty::TyBool |
+            ty::TyChar |
+            ty::TyInt(..) |
+            ty::TyUint(..) |
+            ty::TyFloat(..) |
+            ty::TyAdt(..) |
+            ty::TyStr |
+            ty::TyError |
+            ty::TyArray(..) |
+            ty::TySlice(..) |
+            ty::TyRawPtr(..) |
+            ty::TyRef(..) |
+            ty::TyFnDef(..) |
+            ty::TyFnPtr(_) |
+            ty::TyDynamic(..) |
+            ty::TyNever |
+            ty::TyTuple(..) |
+            ty::TyProjection(..) |
+            ty::TyForeign(..) |
+            ty::TyParam(..) |
+            ty::TyAnon(..) => t.super_fold_with(self),
+        }
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
+    fn canonical_var(
+        &mut self,
+        info: CanonicalVarInfo,
+        cvv: CanonicalVarValue<'tcx>,
+    ) -> CanonicalVar {
+        let Canonicalizer {
+            indices,
+            variables,
+            var_values,
+            ..
+        } = self;
+
+        indices
+            .entry(cvv)
+            .or_insert_with(|| {
+                let cvar1 = variables.push(info);
+                let cvar2 = var_values.push(cvv);
+                assert_eq!(cvar1, cvar2);
+                cvar1
+            })
+            .clone()
+    }
+
+    fn canonicalize_ty_var(
+        &mut self,
+        ty_kind: CanonicalTyVarKind,
+        universe: ty::UniverseIndex,
+        ty_var: Ty<'tcx>,
+    ) -> Ty<'tcx> {
+        let bound_to = self.infcx.shallow_resolve(ty_var);
+        if bound_to != ty_var {
+            self.fold_ty(bound_to)
+        } else {
+            let info = CanonicalVarInfo {
+                kind: CanonicalVarKind::Ty(ty_kind),
+                universe,
+            };
+            let cvar = self.canonical_var(info, CanonicalVarValue::Ty(ty_var));
+            self.tcx().mk_infer(ty::InferTy::CanonicalTy(cvar))
+        }
+    }
+}
diff --git a/src/librustc/traits/recursive_solver/mod.rs b/src/librustc/traits/recursive_solver/mod.rs
new file mode 100644
index 0000000000000..dc888bd402a9c
--- /dev/null
+++ b/src/librustc/traits/recursive_solver/mod.rs
@@ -0,0 +1,13 @@
+// Copyright 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.
+
+//! New recursive solver modeled on Chalk's recursive solver.
+
+mod canonical;
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 0a67661e08aed..fa6dd29404cce 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -1924,9 +1924,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) => None,
             ty::TyInfer(ty::TyVar(_)) => Ambiguous,
 
-            ty::TyInfer(ty::FreshTy(_))
-            | ty::TyInfer(ty::FreshIntTy(_))
-            | ty::TyInfer(ty::FreshFloatTy(_)) => {
+            ty::TyInfer(ty::CanonicalTy(_)) |
+            ty::TyInfer(ty::FreshTy(_)) |
+            ty::TyInfer(ty::FreshIntTy(_)) |
+            ty::TyInfer(ty::FreshFloatTy(_)) => {
                 bug!("asked to assemble builtin bounds of unexpected type: {:?}",
                      self_ty);
             }
@@ -1993,9 +1994,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 Ambiguous
             }
 
-            ty::TyInfer(ty::FreshTy(_))
-            | ty::TyInfer(ty::FreshIntTy(_))
-            | ty::TyInfer(ty::FreshFloatTy(_)) => {
+            ty::TyInfer(ty::CanonicalTy(_)) |
+            ty::TyInfer(ty::FreshTy(_)) |
+            ty::TyInfer(ty::FreshIntTy(_)) |
+            ty::TyInfer(ty::FreshFloatTy(_)) => {
                 bug!("asked to assemble builtin bounds of unexpected type: {:?}",
                      self_ty);
             }
@@ -2034,6 +2036,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             ty::TyParam(..) |
             ty::TyForeign(..) |
             ty::TyProjection(..) |
+            ty::TyInfer(ty::CanonicalTy(_)) |
             ty::TyInfer(ty::TyVar(_)) |
             ty::TyInfer(ty::FreshTy(_)) |
             ty::TyInfer(ty::FreshIntTy(_)) |
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs
index 7b46525544bfd..4e7e13ed51b57 100644
--- a/src/librustc/ty/error.rs
+++ b/src/librustc/ty/error.rs
@@ -215,6 +215,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
             ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(),
             ty::TyInfer(ty::IntVar(_)) => "integral variable".to_string(),
             ty::TyInfer(ty::FloatVar(_)) => "floating-point variable".to_string(),
+            ty::TyInfer(ty::CanonicalTy(_)) |
             ty::TyInfer(ty::FreshTy(_)) => "skolemized type".to_string(),
             ty::TyInfer(ty::FreshIntTy(_)) => "skolemized integral type".to_string(),
             ty::TyInfer(ty::FreshFloatTy(_)) => "skolemized floating-point type".to_string(),
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index ef3a017d369f6..7391b510d6542 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -58,7 +58,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
 
 use hir;
 
-pub use self::sty::{Binder, DebruijnIndex};
+pub use self::sty::{Binder, CanonicalVar, DebruijnIndex};
 pub use self::sty::{FnSig, GenSig, PolyFnSig, PolyGenSig};
 pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
 pub use self::sty::{ClosureSubsts, GeneratorInterior, TypeAndMut};
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index aad1ad0c277fc..da85de7561cdd 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -14,6 +14,7 @@ use hir::def_id::DefId;
 
 use middle::const_val::ConstVal;
 use middle::region;
+use rustc_data_structures::indexed_vec::Idx;
 use ty::subst::{Substs, Subst};
 use ty::{self, AdtDef, TypeFlags, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use ty::{Slice, TyS};
@@ -912,6 +913,9 @@ pub enum RegionKind {
 
     /// Erased region, used by trait selection, in MIR and during trans.
     ReErased,
+
+    /// Canonicalized region, used only when preparing a trait query.
+    ReCanonical(CanonicalVar),
 }
 
 impl<'tcx> serialize::UseSpecializedDecodable for Region<'tcx> {}
@@ -955,8 +959,13 @@ pub enum InferTy {
     FreshTy(u32),
     FreshIntTy(u32),
     FreshFloatTy(u32),
+
+    /// Canonicalized type variable, used only when preparing a trait query.
+    CanonicalTy(CanonicalVar),
 }
 
+newtype_index!(CanonicalVar);
+
 /// A `ProjectionPredicate` for an `ExistentialTraitRef`.
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct ExistentialProjection<'tcx> {
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 1ce1326399124..097956551bb73 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -774,6 +774,7 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W>
             ty::ReFree(..) |
             ty::ReScope(..) |
             ty::ReVar(..) |
+            ty::ReCanonical(..) |
             ty::ReSkolemized(..) => {
                 bug!("TypeIdHasher: unexpected region {:?}", r)
             }
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 9edd2cb5ca183..6d9e6f90da72e 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -709,6 +709,9 @@ define_print! {
                 ty::ReEarlyBound(ref data) => {
                     write!(f, "{}", data.name)
                 }
+                ty::ReCanonical(_) => {
+                    write!(f, "'_")
+                }
                 ty::ReLateBound(_, br) |
                 ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
                 ty::ReSkolemized(_, br) => {
@@ -765,6 +768,10 @@ define_print! {
                     write!(f, "{:?}", vid)
                 }
 
+                ty::ReCanonical(c) => {
+                    write!(f, "'?{}", c.index())
+                }
+
                 ty::ReSkolemized(id, ref bound_region) => {
                     write!(f, "ReSkolemized({:?}, {:?})", id, bound_region)
                 }
@@ -865,6 +872,7 @@ define_print! {
                 ty::TyVar(_) => write!(f, "_"),
                 ty::IntVar(_) => write!(f, "{}", "{integer}"),
                 ty::FloatVar(_) => write!(f, "{}", "{float}"),
+                ty::CanonicalTy(_) => write!(f, "_"),
                 ty::FreshTy(v) => write!(f, "FreshTy({})", v),
                 ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v),
                 ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v)
@@ -875,6 +883,7 @@ define_print! {
                 ty::TyVar(ref v) => write!(f, "{:?}", v),
                 ty::IntVar(ref v) => write!(f, "{:?}", v),
                 ty::FloatVar(ref v) => write!(f, "{:?}", v),
+                ty::CanonicalTy(v) => write!(f, "?{:?}", v.index()),
                 ty::FreshTy(v) => write!(f, "FreshTy({:?})", v),
                 ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v),
                 ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v)
diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
index 8654f2a50e46b..b0e755aa77855 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
@@ -366,6 +366,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
 
                     ty::ReStatic => self.item_ub,
 
+                    ty::ReCanonical(_) |
                     ty::ReEmpty |
                     ty::ReLateBound(..) |
                     ty::ReVar(..) |
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index ce2ac73a27e0c..3e0c888d34473 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -309,6 +309,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
                         gcx.types.re_static
                     }
 
+                    ty::ReCanonical(_) |
                     ty::ReVar(_) |
                     ty::ReErased => {
                         let span = node_id.to_span(&self.fcx.tcx);
diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs
index ef6552c8e33f4..9f18a449f7896 100644
--- a/src/librustc_typeck/variance/constraints.rs
+++ b/src/librustc_typeck/variance/constraints.rs
@@ -464,6 +464,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
                 // way early-bound regions do, so we skip them here.
             }
 
+            ty::ReCanonical(_) |
             ty::ReFree(..) |
             ty::ReScope(..) |
             ty::ReVar(..) |

From 73dcccb9dce6c49e966ab47930fb2144afe81691 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Thu, 9 Nov 2017 04:39:01 -0500
Subject: [PATCH 63/63] WIP introduce recursive solver

---
 src/Cargo.lock                                |  18 +++
 .../recursive_solver => infer}/canonical.rs   |  15 +-
 src/librustc/infer/lib.rs                     | 108 +++++++++++++++
 src/librustc/infer/mod.rs                     |   1 +
 src/librustc/infer/region_inference/mod.rs    |   3 +
 src/librustc/lib.rs                           |   1 +
 src/librustc/traits/mod.rs                    |   1 -
 src/librustc/ty/maps/on_disk_cache.rs         |   2 +-
 src/librustc_data_structures/indexed_vec.rs   |   2 +-
 src/librustc_driver/Cargo.toml                |   1 +
 src/librustc_traits/Cargo.toml                |  22 +++
 .../mod.rs => librustc_traits/fallible.rs}    |  10 +-
 src/librustc_traits/lib.rs                    | 107 ++++++++++++++
 src/librustc_traits/search_graph.rs           | 131 ++++++++++++++++++
 src/librustc_traits/stack.rs                  | 116 ++++++++++++++++
 15 files changed, 527 insertions(+), 11 deletions(-)
 rename src/librustc/{traits/recursive_solver => infer}/canonical.rs (97%)
 create mode 100644 src/librustc/infer/lib.rs
 create mode 100644 src/librustc_traits/Cargo.toml
 rename src/{librustc/traits/recursive_solver/mod.rs => librustc_traits/fallible.rs} (66%)
 create mode 100644 src/librustc_traits/lib.rs
 create mode 100644 src/librustc_traits/search_graph.rs
 create mode 100644 src/librustc_traits/stack.rs

diff --git a/src/Cargo.lock b/src/Cargo.lock
index a0db98f95b0c3..2276c18ba1a23 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -1688,6 +1688,7 @@ dependencies = [
  "rustc_privacy 0.0.0",
  "rustc_resolve 0.0.0",
  "rustc_save_analysis 0.0.0",
+ "rustc_traits 0.0.0",
  "rustc_trans 0.0.0",
  "rustc_trans_utils 0.0.0",
  "rustc_typeck 0.0.0",
@@ -1864,6 +1865,23 @@ dependencies = [
  "syntax_pos 0.0.0",
 ]
 
+[[package]]
+name = "rustc_traits"
+version = "0.0.0"
+dependencies = [
+ "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "graphviz 0.0.0",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc 0.0.0",
+ "rustc_const_eval 0.0.0",
+ "rustc_const_math 0.0.0",
+ "rustc_data_structures 0.0.0",
+ "rustc_errors 0.0.0",
+ "serialize 0.0.0",
+ "syntax 0.0.0",
+ "syntax_pos 0.0.0",
+]
+
 [[package]]
 name = "rustc_trans"
 version = "0.0.0"
diff --git a/src/librustc/traits/recursive_solver/canonical.rs b/src/librustc/infer/canonical.rs
similarity index 97%
rename from src/librustc/traits/recursive_solver/canonical.rs
rename to src/librustc/infer/canonical.rs
index 376cf63c2ff83..f23fe008a5b8c 100644
--- a/src/librustc/traits/recursive_solver/canonical.rs
+++ b/src/librustc/infer/canonical.rs
@@ -12,13 +12,14 @@ use infer::InferCtxt;
 use hir::def_id::DefId;
 use ty::{self, CanonicalVar, Ty, TyCtxt};
 use ty::fold::{TypeFoldable, TypeFolder};
+
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_data_structures::fx::FxHashMap;
 
 /// A "canonicalized" type `V` is one where all free inference
 /// variables have been rewriten to "canonical vars". These are
 /// numbered starting from 0 in order of first appearance.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub struct Canonical<V> {
     pub variables: IndexVec<CanonicalVar, CanonicalVarInfo>,
     pub value: V,
@@ -161,6 +162,10 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx>
                 let cvar = self.canonical_var(info, CanonicalVarValue::Region(r));
                 self.tcx().mk_region(ty::ReCanonical(cvar))
             }
+
+            ty::ReCanonical(_) => {
+                bug!("canonical region encountered during canonicalization")
+            }
         }
     }
 
@@ -171,10 +176,8 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx>
             return t;
         }
 
-        let tcx = self.infcx.tcx;
-
         match t.sty {
-            ty::TyInfer(ty::TyVar(v)) => {
+            ty::TyInfer(ty::TyVar(_)) => {
                 self.canonicalize_ty_var(
                     CanonicalTyVarKind::General,
                     ty::UniverseIndex::ROOT, // TODO
@@ -182,7 +185,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx>
                 )
             }
 
-            ty::TyInfer(ty::IntVar(v)) => {
+            ty::TyInfer(ty::IntVar(_)) => {
                 self.canonicalize_ty_var(
                     CanonicalTyVarKind::Int,
                     ty::UniverseIndex::ROOT, // TODO
@@ -190,7 +193,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx>
                 )
             }
 
-            ty::TyInfer(ty::FloatVar(v)) => {
+            ty::TyInfer(ty::FloatVar(_)) => {
                 self.canonicalize_ty_var(
                     CanonicalTyVarKind::Float,
                     ty::UniverseIndex::ROOT, // TODO
diff --git a/src/librustc/infer/lib.rs b/src/librustc/infer/lib.rs
new file mode 100644
index 0000000000000..0689e37632d0e
--- /dev/null
+++ b/src/librustc/infer/lib.rs
@@ -0,0 +1,108 @@
+// Copyright 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.
+
+//! New recursive solver modeled on Chalk's recursive solver. Most of
+//! the guts are broken up into modules; see the comments in those modules.
+
+#![feature(match_default_bindings)]
+#![feature(underscore_lifetimes)]
+
+#[macro_use] extern crate log;
+#[macro_use] extern crate rustc;
+extern crate rustc_data_structures;
+
+use rustc::infer::InferCtxt;
+use rustc::ty::{self, CanonicalVar};
+use rustc_data_structures::fx::FxHashMap;
+use std::collections::BTreeMap;
+
+mod canonical;
+use self::canonical::{Canonical, CanonicalVarValue};
+
+mod fallible;
+use self::fallible::Fallible;
+
+mod search_graph;
+use self::search_graph::{DepthFirstNumber, SearchGraph};
+
+mod stack;
+use self::stack::Stack;
+
+/// A Solver is the basic context in which you can propose goals for a given
+/// program. **All questions posed to the solver are in canonical, closed form,
+/// so that each question is answered with effectively a "clean slate"**. This
+/// allows for better caching, and simplifies management of the inference
+/// context.
+pub struct Solver<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    stack: Stack,
+    search_graph: SearchGraph<'tcx>,
+
+    caching_enabled: bool,
+
+    /// The cache contains **fully solved** goals, whose results are
+    /// not dependent on the stack in anyway.
+    cache: FxHashMap<PredicateAtomGoal<'tcx>, Fallible<Solution<'tcx>>>,
+}
+
+/// Internally to the solver, a `Goal` is used to mean something we
+/// are trying to prove. It is like an obligation, but stripped of
+/// the `cause`.
+struct Goal<'tcx, G> {
+    param_env: ty::ParamEnv<'tcx>,
+    goal: G,
+}
+
+/// The main top-line goals we try to prove.
+type PredicateAtomGoal<'tcx> = Goal<'tcx, ty::PredicateAtom<'tcx>>;
+
+/// The `minimums` struct is used while solving to track whether we encountered
+/// any cycles in the process.
+#[derive(Copy, Clone, Debug)]
+struct Minimums {
+    positive: DepthFirstNumber,
+}
+
+impl Minimums {
+    fn new() -> Self {
+        Minimums {
+            positive: DepthFirstNumber::MAX,
+        }
+    }
+
+    fn update_from(&mut self, minimums: Minimums) {
+        self.positive = ::std::cmp::min(self.positive, minimums.positive);
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+enum Solution<'tcx> {
+    Ambiguous,
+    Unique(Canonical<ConstrainedCanonicalVarSubstitution<'tcx>>),
+}
+
+/// Combines a `CanonicalVarSubstitution` with region predicates.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+struct ConstrainedCanonicalVarSubstitution<'tcx> {
+    pub subst: CanonicalVarSubstitution<'tcx>,
+    // TODO pub constraints: Vec<InEnvironment<Constraint>>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+struct CanonicalVarSubstitution<'tcx> {
+    /// Map free variable with given index to the value with the same
+    /// index. Naturally, the kind of the variable must agree with
+    /// the kind of the value.
+    ///
+    /// This is a map because the substitution is not necessarily
+    /// complete. We use a btree map to ensure that the result is in a
+    /// deterministic order.
+    pub parameters: BTreeMap<CanonicalVar, CanonicalVarValue<'tcx>>,
+}
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index c46fa0b955a2b..1ac53cac54c9a 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -44,6 +44,7 @@ use self::type_variable::TypeVariableOrigin;
 use self::unify_key::ToType;
 
 pub mod at;
+pub mod canonical;
 mod combine;
 mod equate;
 pub mod error_reporting;
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index 9d524947a346c..a1d7a93aae77c 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -661,6 +661,9 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
             ty::ReLateBound(..) =>
                 bug!("universe(): encountered bound region {:?}", region),
+
+            ty::ReCanonical(..) =>
+                bug!("universe(): encountered canonical region {:?}", region),
         }
     }
 
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 965905add20c3..e160c513bc5dc 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -57,6 +57,7 @@
 #![feature(slice_patterns)]
 #![feature(specialization)]
 #![feature(unboxed_closures)]
+#![feature(underscore_lifetimes)]
 #![feature(trace_macros)]
 #![feature(test)]
 #![feature(const_atomic_bool_new)]
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 9b7c6069a544b..eb83816f7524e 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -54,7 +54,6 @@ mod fulfill;
 mod project;
 mod object_safety;
 mod on_unimplemented;
-mod recursive_solver;
 mod select;
 mod specialize;
 mod structural_impls;
diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs
index 26581501234af..24ce8fb299598 100644
--- a/src/librustc/ty/maps/on_disk_cache.rs
+++ b/src/librustc/ty/maps/on_disk_cache.rs
@@ -165,7 +165,7 @@ impl<'a> CacheDecoder<'a> {
     fn find_filemap_prev_bytepos(&self,
                                  prev_bytepos: BytePos)
                                  -> Option<(BytePos, StableFilemapId)> {
-        for (start, id) in self.prev_filemap_starts.range(BytePos(0) ... prev_bytepos).rev() {
+        for (start, id) in self.prev_filemap_starts.range(BytePos(0) ..= prev_bytepos).rev() {
             return Some((*start, *id))
         }
 
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
index a733e9de5a1ab..473925524b415 100644
--- a/src/librustc_data_structures/indexed_vec.rs
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -324,7 +324,7 @@ macro_rules! newtype_index {
     );
 }
 
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq, Hash)]
 pub struct IndexVec<I: Idx, T> {
     pub raw: Vec<T>,
     _marker: PhantomData<Fn(&I)>
diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml
index d6155f53485e3..717695b84620a 100644
--- a/src/librustc_driver/Cargo.toml
+++ b/src/librustc_driver/Cargo.toml
@@ -30,6 +30,7 @@ rustc_plugin = { path = "../librustc_plugin" }
 rustc_privacy = { path = "../librustc_privacy" }
 rustc_resolve = { path = "../librustc_resolve" }
 rustc_save_analysis = { path = "../librustc_save_analysis" }
+rustc_traits = { path = "../librustc_traits" }
 rustc_trans = { path = "../librustc_trans", optional = true }
 rustc_trans_utils = { path = "../librustc_trans_utils" }
 rustc_typeck = { path = "../librustc_typeck" }
diff --git a/src/librustc_traits/Cargo.toml b/src/librustc_traits/Cargo.toml
new file mode 100644
index 0000000000000..fe83344e69952
--- /dev/null
+++ b/src/librustc_traits/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_traits"
+version = "0.0.0"
+
+[lib]
+name = "rustc_traits"
+path = "lib.rs"
+crate-type = ["dylib"]
+
+[dependencies]
+bitflags = "1.0"
+graphviz = { path = "../libgraphviz" }
+log = "0.3"
+rustc = { path = "../librustc" }
+rustc_const_eval = { path = "../librustc_const_eval" }
+rustc_const_math = { path = "../librustc_const_math" }
+rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
+serialize = { path = "../libserialize" }
+syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
diff --git a/src/librustc/traits/recursive_solver/mod.rs b/src/librustc_traits/fallible.rs
similarity index 66%
rename from src/librustc/traits/recursive_solver/mod.rs
rename to src/librustc_traits/fallible.rs
index dc888bd402a9c..4842b30e630ba 100644
--- a/src/librustc/traits/recursive_solver/mod.rs
+++ b/src/librustc_traits/fallible.rs
@@ -8,6 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! New recursive solver modeled on Chalk's recursive solver.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct NoSolution;
 
-mod canonical;
+pub type Fallible<T> = Result<T, NoSolution>;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Overflow;
+
+pub type CanOverflow<T> = Result<T, Overflow>;
diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs
new file mode 100644
index 0000000000000..619e43dbc3c9f
--- /dev/null
+++ b/src/librustc_traits/lib.rs
@@ -0,0 +1,107 @@
+// Copyright 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.
+
+//! New recursive solver modeled on Chalk's recursive solver. Most of
+//! the guts are broken up into modules; see the comments in those modules.
+
+#![feature(crate_visibility_modifier)]
+#![feature(underscore_lifetimes)]
+
+#[macro_use] extern crate log;
+extern crate rustc;
+extern crate rustc_data_structures;
+
+use rustc::infer::InferCtxt;
+use rustc::infer::canonical::{Canonical, CanonicalVarValue};
+use rustc::ty::{self, CanonicalVar};
+use rustc_data_structures::fx::FxHashMap;
+use std::collections::BTreeMap;
+
+mod fallible;
+use self::fallible::Fallible;
+
+mod search_graph;
+use self::search_graph::{DepthFirstNumber, SearchGraph};
+
+mod stack;
+use self::stack::Stack;
+
+/// A Solver is the basic context in which you can propose goals for a given
+/// program. **All questions posed to the solver are in canonical, closed form,
+/// so that each question is answered with effectively a "clean slate"**. This
+/// allows for better caching, and simplifies management of the inference
+/// context.
+pub struct Solver<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    stack: Stack,
+    search_graph: SearchGraph<'tcx>,
+
+    caching_enabled: bool,
+
+    /// The cache contains **fully solved** goals, whose results are
+    /// not dependent on the stack in anyway.
+    cache: FxHashMap<PredicateAtomGoal<'tcx>, Fallible<Solution<'tcx>>>,
+}
+
+/// Internally to the solver, a `Goal` is used to mean something we
+/// are trying to prove. It is like an obligation, but stripped of
+/// the `cause`.
+struct Goal<'tcx, G> {
+    param_env: ty::ParamEnv<'tcx>,
+    goal: G,
+}
+
+/// The main top-line goals we try to prove.
+type PredicateAtomGoal<'tcx> = Goal<'tcx, ty::PredicateAtom<'tcx>>;
+
+/// The `minimums` struct is used while solving to track whether we encountered
+/// any cycles in the process.
+#[derive(Copy, Clone, Debug)]
+struct Minimums {
+    positive: DepthFirstNumber,
+}
+
+impl Minimums {
+    fn new() -> Self {
+        Minimums {
+            positive: DepthFirstNumber::MAX,
+        }
+    }
+
+    fn update_from(&mut self, minimums: Minimums) {
+        self.positive = ::std::cmp::min(self.positive, minimums.positive);
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+enum Solution<'tcx> {
+    Ambiguous,
+    Unique(Canonical<ConstrainedCanonicalVarSubstitution<'tcx>>),
+}
+
+/// Combines a `CanonicalVarSubstitution` with region predicates.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+struct ConstrainedCanonicalVarSubstitution<'tcx> {
+    pub subst: CanonicalVarSubstitution<'tcx>,
+    // TODO pub constraints: Vec<InEnvironment<Constraint>>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+struct CanonicalVarSubstitution<'tcx> {
+    /// Map free variable with given index to the value with the same
+    /// index. Naturally, the kind of the variable must agree with
+    /// the kind of the value.
+    ///
+    /// This is a map because the substitution is not necessarily
+    /// complete. We use a btree map to ensure that the result is in a
+    /// deterministic order.
+    pub parameters: BTreeMap<CanonicalVar, CanonicalVarValue<'tcx>>,
+}
+
diff --git a/src/librustc_traits/search_graph.rs b/src/librustc_traits/search_graph.rs
new file mode 100644
index 0000000000000..ce3aea4b4c232
--- /dev/null
+++ b/src/librustc_traits/search_graph.rs
@@ -0,0 +1,131 @@
+// Copyright 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.
+
+//! The search graph encodes the proof search that we are currently
+//! doing. When cycles are encountered, it rolls back.
+
+use ::fallible::*;
+use ::stack::StackDepth;
+use ::{Minimums, Solution};
+
+use rustc::ty;
+use rustc_data_structures::fx::FxHashMap;
+use std::ops::{Add, Index, IndexMut};
+
+pub(super) struct SearchGraph<'tcx> {
+    indices: FxHashMap<ty::PredicateAtom<'tcx>, DepthFirstNumber>,
+    nodes: Vec<Node<'tcx>>,
+}
+
+#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
+pub(super) struct DepthFirstNumber {
+    index: usize,
+}
+
+pub(super) struct Node<'tcx> {
+    pub goal: ty::PredicateAtom<'tcx>,
+
+    pub solution: Fallible<Solution<'tcx>>,
+
+    /// This is `Some(X)` if we are actively exploring this node, or
+    /// `None` otherwise.
+    pub stack_depth: Option<StackDepth>,
+
+    /// While this node is on the stack, this field will be set to
+    /// contain our own depth-first number. Once the node is popped
+    /// from the stack, it contains the DFN of the minimal ancestor
+    /// that the table reached (or MAX if no cycle was encountered).
+    pub links: Minimums,
+}
+
+impl<'tcx> SearchGraph<'tcx> {
+    pub fn new() -> Self {
+        SearchGraph {
+            indices: FxHashMap::default(),
+            nodes: vec![],
+        }
+    }
+
+    pub fn lookup(&self, goal: &ty::PredicateAtom<'tcx>) -> Option<DepthFirstNumber> {
+        self.indices.get(goal).cloned()
+    }
+
+    /// Insert a new search node in the tree. The node will be in the initial
+    /// state for a search node:
+    ///
+    /// - stack depth as given
+    /// - links set to its own DFN
+    /// - solution is initially `NoSolution`
+    pub fn insert(&mut self, goal: &ty::PredicateAtom<'tcx>, stack_depth: StackDepth) -> DepthFirstNumber {
+        let dfn = DepthFirstNumber {
+            index: self.nodes.len(),
+        };
+        let node = Node {
+            goal: goal.clone(),
+            solution: Err(NoSolution),
+            stack_depth: Some(stack_depth),
+            links: Minimums { positive: dfn },
+        };
+        self.nodes.push(node);
+        let previous_index = self.indices.insert(goal.clone(), dfn);
+        assert!(previous_index.is_none());
+        dfn
+    }
+
+    /// Clears all nodes with a depth-first number greater than or equal `dfn`.
+    pub fn rollback_to(&mut self, dfn: DepthFirstNumber) {
+        debug!("rollback_to(dfn={:?})", dfn);
+        self.indices.retain(|_key, value| *value < dfn);
+        self.nodes.truncate(dfn.index);
+    }
+
+    /// Removes all nodes with a depth-first-number greater than or
+    /// equal to `dfn`, adding their final solutions into the cache.
+    pub fn move_to_cache(
+        &mut self,
+        dfn: DepthFirstNumber,
+        cache: &mut FxHashMap<ty::PredicateAtom<'tcx>, Fallible<Solution<'tcx>>>,
+    ) {
+        debug!("move_to_cache(dfn={:?})", dfn);
+        self.indices.retain(|_key, value| *value < dfn);
+        for node in self.nodes.drain(dfn.index..) {
+            assert!(node.stack_depth.is_none());
+            assert!(node.links.positive >= dfn);
+            debug!("caching solution {:?} for {:?}", node.solution, node.goal);
+            cache.insert(node.goal, node.solution);
+        }
+    }
+}
+
+impl<'tcx> Index<DepthFirstNumber> for SearchGraph<'tcx> {
+    type Output = Node<'tcx>;
+
+    fn index(&self, table_index: DepthFirstNumber) -> &Node<'tcx> {
+        &self.nodes[table_index.index]
+    }
+}
+
+impl<'tcx> IndexMut<DepthFirstNumber> for SearchGraph<'tcx> {
+    fn index_mut(&mut self, table_index: DepthFirstNumber) -> &mut Node<'tcx> {
+        &mut self.nodes[table_index.index]
+    }
+}
+
+impl DepthFirstNumber {
+    pub const MAX: DepthFirstNumber = DepthFirstNumber { index: ::std::usize::MAX };
+}
+
+impl Add<usize> for DepthFirstNumber {
+    type Output = DepthFirstNumber;
+
+    fn add(self, v: usize) -> DepthFirstNumber {
+        DepthFirstNumber { index: self.index + v }
+    }
+}
diff --git a/src/librustc_traits/stack.rs b/src/librustc_traits/stack.rs
new file mode 100644
index 0000000000000..82ff5a3622dcf
--- /dev/null
+++ b/src/librustc_traits/stack.rs
@@ -0,0 +1,116 @@
+use ::fallible::*;
+use ::PredicateAtomGoal;
+
+use rustc::infer::InferCtxt;
+use rustc::ty;
+use std::mem;
+use std::ops::Index;
+use std::ops::IndexMut;
+use std::usize;
+
+pub(crate) struct Stack {
+    entries: Vec<StackEntry>,
+    overflow_depth: usize,
+}
+
+#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
+pub struct StackDepth {
+    depth: usize,
+}
+
+/// The data we actively keep for each goal on the stack.
+pub struct StackEntry {
+    /// Was this a coinductive goal?
+    coinductive_goal: bool,
+
+    /// Initially false, set to true when some subgoal depends on us.
+    cycle: bool,
+}
+
+impl<'tcx> Stack {
+    pub(crate) fn new(overflow_depth: usize) -> Self {
+        Stack {
+            entries: vec![],
+            overflow_depth: overflow_depth,
+        }
+    }
+
+    pub(crate) fn is_empty(&self) -> bool {
+        self.entries.is_empty()
+    }
+
+    pub(crate) fn push(&mut self,
+                infcx: &InferCtxt<'_, '_, 'tcx>,
+                goal: &PredicateAtomGoal<'tcx>)
+                -> CanOverflow<StackDepth> {
+        let depth = StackDepth {
+            depth: self.entries.len(),
+        };
+
+        if depth.depth >= self.overflow_depth {
+            // This should perhaps be a result or something, though
+            // really I'd prefer to move to subgoal abstraction for
+            // guaranteeing termination. -nmatsakis
+            return Err(Overflow);
+        }
+
+        let coinductive_goal = self.coinductive_predicate(infcx, goal.goal);
+        self.entries.push(StackEntry { coinductive_goal, cycle: false });
+        Ok(depth)
+    }
+
+    fn coinductive_predicate(&self,
+                             infcx: &InferCtxt<'_, '_, 'tcx>,
+                             predicate: ty::PredicateAtom<'tcx>)
+                             -> bool {
+        let result = match predicate {
+            ty::PredicateAtom::Trait(ref data) => {
+                infcx.tcx.trait_is_auto(data.def_id)
+            }
+            _ => {
+                false
+            }
+        };
+        debug!("coinductive_predicate({:?}) = {:?}", predicate, result);
+        result
+    }
+
+    pub(crate) fn pop(&mut self, depth: StackDepth) {
+        assert_eq!(
+            depth.depth + 1,
+            self.entries.len(),
+            "mismatched stack push/pop"
+        );
+        self.entries.pop();
+    }
+
+    /// True if all the goals from the top of the stack down to (and
+    /// including) the given depth are coinductive.
+    pub(crate) fn coinductive_cycle_from(&self, depth: StackDepth) -> bool {
+        self.entries[depth.depth..].iter().all(|entry| entry.coinductive_goal)
+    }
+}
+
+impl StackEntry {
+    pub(crate) fn flag_cycle(&mut self) {
+        self.cycle = true;
+    }
+
+    pub(crate) fn read_and_reset_cycle_flag(&mut self) -> bool {
+        mem::replace(&mut self.cycle, false)
+    }
+}
+
+impl Index<StackDepth> for Stack {
+    type Output = StackEntry;
+
+    fn index(&self, depth: StackDepth) -> &StackEntry {
+        &self.entries[depth.depth]
+    }
+}
+
+impl IndexMut<StackDepth> for Stack {
+    fn index_mut(&mut self, depth: StackDepth) -> &mut StackEntry {
+        &mut self.entries[depth.depth]
+    }
+}