about summary refs log tree commit diff
path: root/compiler/rustc_infer
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_infer')
-rw-r--r--compiler/rustc_infer/Cargo.toml21
-rw-r--r--compiler/rustc_infer/src/infer/at.rs334
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs649
-rw-r--r--compiler/rustc_infer/src/infer/canonical/mod.rs163
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs692
-rw-r--r--compiler/rustc_infer/src/infer/canonical/substitute.rs91
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs992
-rw-r--r--compiler/rustc_infer/src/infer/equate.rs149
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs2578
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs858
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs191
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs274
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs103
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs75
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs130
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs493
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs518
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs151
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs184
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs396
-rw-r--r--compiler/rustc_infer/src/infer/free_regions.rs160
-rw-r--r--compiler/rustc_infer/src/infer/freshen.rs266
-rw-r--r--compiler/rustc_infer/src/infer/fudge.rs249
-rw-r--r--compiler/rustc_infer/src/infer/glb.rs126
-rw-r--r--compiler/rustc_infer/src/infer/higher_ranked/README.md8
-rw-r--r--compiler/rustc_infer/src/infer/higher_ranked/mod.rs150
-rw-r--r--compiler/rustc_infer/src/infer/lattice.rs99
-rw-r--r--compiler/rustc_infer/src/infer/lexical_region_resolve/README.md6
-rw-r--r--compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs1020
-rw-r--r--compiler/rustc_infer/src/infer/lub.rs126
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs1768
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs1035
-rw-r--r--compiler/rustc_infer/src/infer/outlives/env.rs195
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs34
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs468
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs355
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/README.md3
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/leak_check.rs446
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/mod.rs812
-rw-r--r--compiler/rustc_infer/src/infer/resolve.rs250
-rw-r--r--compiler/rustc_infer/src/infer/sub.rs180
-rw-r--r--compiler/rustc_infer/src/infer/type_variable.rs446
-rw-r--r--compiler/rustc_infer/src/infer/undo_log.rs212
-rw-r--r--compiler/rustc_infer/src/lib.rs38
-rw-r--r--compiler/rustc_infer/src/traits/engine.rs78
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs114
-rw-r--r--compiler/rustc_infer/src/traits/mod.rs139
-rw-r--r--compiler/rustc_infer/src/traits/project.rs226
-rw-r--r--compiler/rustc_infer/src/traits/structural_impls.rs75
-rw-r--r--compiler/rustc_infer/src/traits/util.rs354
50 files changed, 18480 insertions, 0 deletions
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
new file mode 100644
index 00000000000..15649bb678e
--- /dev/null
+++ b/compiler/rustc_infer/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "rustc_infer"
+version = "0.0.0"
+edition = "2018"
+
+[lib]
+doctest = false
+
+[dependencies]
+tracing = "0.1"
+rustc_middle = { path = "../rustc_middle" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_index = { path = "../rustc_index" }
+rustc_macros = { path = "../rustc_macros" }
+rustc_session = { path = "../rustc_session" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
+smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
new file mode 100644
index 00000000000..11ee8fb17ad
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -0,0 +1,334 @@
+//! A nice interface for working with the infcx. The basic idea is to
+//! do `infcx.at(cause, param_env)`, which sets the "cause" of the
+//! operation as well as the surrounding parameter environment. Then
+//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a
+//! subtype or equality relationship respectively. The first argument
+//! is always the "expected" output from the POV of diagnostics.
+//!
+//! Examples:
+//!
+//!     infcx.at(cause, param_env).sub(a, b)
+//!     // requires that `a <: b`, with `a` considered the "expected" type
+//!
+//!     infcx.at(cause, param_env).sup(a, b)
+//!     // requires that `b <: a`, with `a` considered the "expected" type
+//!
+//!     infcx.at(cause, param_env).eq(a, b)
+//!     // requires that `a == b`, with `a` considered the "expected" type
+//!
+//! For finer-grained control, you can also do use `trace`:
+//!
+//!     infcx.at(...).trace(a, b).sub(&c, &d)
+//!
+//! This will set `a` and `b` as the "root" values for
+//! error-reporting, but actually operate on `c` and `d`. This is
+//! sometimes useful when the types of `c` and `d` are not traceable
+//! things. (That system should probably be refactored.)
+
+use super::*;
+
+use rustc_middle::ty::relate::{Relate, TypeRelation};
+use rustc_middle::ty::Const;
+
+pub struct At<'a, 'tcx> {
+    pub infcx: &'a InferCtxt<'a, 'tcx>,
+    pub cause: &'a ObligationCause<'tcx>,
+    pub param_env: ty::ParamEnv<'tcx>,
+}
+
+pub struct Trace<'a, 'tcx> {
+    at: At<'a, 'tcx>,
+    a_is_expected: bool,
+    trace: TypeTrace<'tcx>,
+}
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    #[inline]
+    pub fn at(
+        &'a self,
+        cause: &'a ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> At<'a, 'tcx> {
+        At { infcx: self, cause, param_env }
+    }
+}
+
+pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
+    fn to_trace(
+        tcx: TyCtxt<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: Self,
+        b: Self,
+    ) -> TypeTrace<'tcx>;
+}
+
+impl<'a, 'tcx> At<'a, 'tcx> {
+    /// Hacky routine for equating two impl headers in coherence.
+    pub fn eq_impl_headers(
+        self,
+        expected: &ty::ImplHeader<'tcx>,
+        actual: &ty::ImplHeader<'tcx>,
+    ) -> InferResult<'tcx, ()> {
+        debug!("eq_impl_header({:?} = {:?})", expected, actual);
+        match (expected.trait_ref, actual.trait_ref) {
+            (Some(a_ref), Some(b_ref)) => self.eq(a_ref, b_ref),
+            (None, None) => self.eq(expected.self_ty, actual.self_ty),
+            _ => bug!("mk_eq_impl_headers given mismatched impl kinds"),
+        }
+    }
+
+    /// Makes `a <: b`, where `a` may or may not be expected.
+    pub fn sub_exp<T>(self, a_is_expected: bool, a: T, b: T) -> InferResult<'tcx, ()>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.trace_exp(a_is_expected, a, b).sub(a, b)
+    }
+
+    /// Makes `actual <: expected`. For example, if type-checking a
+    /// call like `foo(x)`, where `foo: fn(i32)`, you might have
+    /// `sup(i32, x)`, since the "expected" type is the type that
+    /// appears in the signature.
+    pub fn sup<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.sub_exp(false, actual, expected)
+    }
+
+    /// Makes `expected <: actual`.
+    pub fn sub<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.sub_exp(true, expected, actual)
+    }
+
+    /// Makes `expected <: actual`.
+    pub fn eq_exp<T>(self, a_is_expected: bool, a: T, b: T) -> InferResult<'tcx, ()>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.trace_exp(a_is_expected, a, b).eq(a, b)
+    }
+
+    /// Makes `expected <: actual`.
+    pub fn eq<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.trace(expected, actual).eq(expected, actual)
+    }
+
+    pub fn relate<T>(self, expected: T, variance: ty::Variance, actual: T) -> InferResult<'tcx, ()>
+    where
+        T: ToTrace<'tcx>,
+    {
+        match variance {
+            ty::Variance::Covariant => self.sub(expected, actual),
+            ty::Variance::Invariant => self.eq(expected, actual),
+            ty::Variance::Contravariant => self.sup(expected, actual),
+
+            // We could make this make sense but it's not readily
+            // exposed and I don't feel like dealing with it. Note
+            // that bivariance in general does a bit more than just
+            // *nothing*, it checks that the types are the same
+            // "modulo variance" basically.
+            ty::Variance::Bivariant => panic!("Bivariant given to `relate()`"),
+        }
+    }
+
+    /// Computes the least-upper-bound, or mutual supertype, of two
+    /// values. The order of the arguments doesn't matter, but since
+    /// this can result in an error (e.g., if asked to compute LUB of
+    /// u32 and i32), it is meaningful to call one of them the
+    /// "expected type".
+    pub fn lub<T>(self, expected: T, actual: T) -> InferResult<'tcx, T>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.trace(expected, actual).lub(expected, actual)
+    }
+
+    /// Computes the greatest-lower-bound, or mutual subtype, of two
+    /// values. As with `lub` order doesn't matter, except for error
+    /// cases.
+    pub fn glb<T>(self, expected: T, actual: T) -> InferResult<'tcx, T>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.trace(expected, actual).glb(expected, actual)
+    }
+
+    /// Sets the "trace" values that will be used for
+    /// error-reporting, but doesn't actually perform any operation
+    /// yet (this is useful when you want to set the trace using
+    /// distinct values from those you wish to operate upon).
+    pub fn trace<T>(self, expected: T, actual: T) -> Trace<'a, 'tcx>
+    where
+        T: ToTrace<'tcx>,
+    {
+        self.trace_exp(true, expected, actual)
+    }
+
+    /// Like `trace`, but the expected value is determined by the
+    /// boolean argument (if true, then the first argument `a` is the
+    /// "expected" value).
+    pub fn trace_exp<T>(self, a_is_expected: bool, a: T, b: T) -> Trace<'a, 'tcx>
+    where
+        T: ToTrace<'tcx>,
+    {
+        let trace = ToTrace::to_trace(self.infcx.tcx, self.cause, a_is_expected, a, b);
+        Trace { at: self, trace, a_is_expected }
+    }
+}
+
+impl<'a, 'tcx> Trace<'a, 'tcx> {
+    /// Makes `a <: b` where `a` may or may not be expected (if
+    /// `a_is_expected` is true, then `a` is expected).
+    pub fn sub<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
+    where
+        T: Relate<'tcx>,
+    {
+        debug!("sub({:?} <: {:?})", a, b);
+        let Trace { at, trace, a_is_expected } = self;
+        at.infcx.commit_if_ok(|_| {
+            let mut fields = at.infcx.combine_fields(trace, at.param_env);
+            fields
+                .sub(a_is_expected)
+                .relate(a, b)
+                .map(move |_| InferOk { value: (), obligations: fields.obligations })
+        })
+    }
+
+    /// Makes `a == b`; the expectation is set by the call to
+    /// `trace()`.
+    pub fn eq<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
+    where
+        T: Relate<'tcx>,
+    {
+        debug!("eq({:?} == {:?})", a, b);
+        let Trace { at, trace, a_is_expected } = self;
+        at.infcx.commit_if_ok(|_| {
+            let mut fields = at.infcx.combine_fields(trace, at.param_env);
+            fields
+                .equate(a_is_expected)
+                .relate(a, b)
+                .map(move |_| InferOk { value: (), obligations: fields.obligations })
+        })
+    }
+
+    pub fn lub<T>(self, a: T, b: T) -> InferResult<'tcx, T>
+    where
+        T: Relate<'tcx>,
+    {
+        debug!("lub({:?} \\/ {:?})", a, b);
+        let Trace { at, trace, a_is_expected } = self;
+        at.infcx.commit_if_ok(|_| {
+            let mut fields = at.infcx.combine_fields(trace, at.param_env);
+            fields
+                .lub(a_is_expected)
+                .relate(a, b)
+                .map(move |t| InferOk { value: t, obligations: fields.obligations })
+        })
+    }
+
+    pub fn glb<T>(self, a: T, b: T) -> InferResult<'tcx, T>
+    where
+        T: Relate<'tcx>,
+    {
+        debug!("glb({:?} /\\ {:?})", a, b);
+        let Trace { at, trace, a_is_expected } = self;
+        at.infcx.commit_if_ok(|_| {
+            let mut fields = at.infcx.combine_fields(trace, at.param_env);
+            fields
+                .glb(a_is_expected)
+                .relate(a, b)
+                .map(move |t| InferOk { value: t, obligations: fields.obligations })
+        })
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
+    fn to_trace(
+        _: TyCtxt<'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)) }
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> {
+    fn to_trace(
+        _: TyCtxt<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: Self,
+        b: Self,
+    ) -> TypeTrace<'tcx> {
+        TypeTrace { cause: cause.clone(), values: Regions(ExpectedFound::new(a_is_expected, a, b)) }
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for &'tcx Const<'tcx> {
+    fn to_trace(
+        _: TyCtxt<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: Self,
+        b: Self,
+    ) -> TypeTrace<'tcx> {
+        TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) }
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {
+    fn to_trace(
+        _: TyCtxt<'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)),
+        }
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for ty::PolyTraitRef<'tcx> {
+    fn to_trace(
+        _: TyCtxt<'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)),
+        }
+    }
+}
+
+impl<'tcx> ToTrace<'tcx> for ty::ProjectionTy<'tcx> {
+    fn to_trace(
+        tcx: TyCtxt<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: Self,
+        b: Self,
+    ) -> TypeTrace<'tcx> {
+        let a_ty = tcx.mk_projection(a.item_def_id, a.substs);
+        let b_ty = tcx.mk_projection(b.item_def_id, b.substs);
+        TypeTrace {
+            cause: cause.clone(),
+            values: Types(ExpectedFound::new(a_is_expected, a_ty, b_ty)),
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
new file mode 100644
index 00000000000..448dd662348
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -0,0 +1,649 @@
+//! This module contains the "canonicalizer" itself.
+//!
+//! For an overview of what canonicalization is and how it fits into
+//! rustc, check out the [chapter in the rustc dev guide][c].
+//!
+//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
+
+use crate::infer::canonical::{
+    Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized,
+    OriginalQueryValues,
+};
+use crate::infer::InferCtxt;
+use rustc_middle::ty::flags::FlagComputation;
+use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
+use rustc_middle::ty::subst::GenericArg;
+use rustc_middle::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags};
+use std::sync::atomic::Ordering;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_index::vec::Idx;
+use smallvec::SmallVec;
+
+impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
+    /// Canonicalizes a query value `V`. When we canonicalize a query,
+    /// we not only canonicalize unbound inference variables, but we
+    /// *also* replace all free regions whatsoever. So for example a
+    /// query like `T: Trait<'static>` would be canonicalized to
+    ///
+    /// ```text
+    /// T: Trait<'?0>
+    /// ```
+    ///
+    /// with a mapping M that maps `'?0` to `'static`.
+    ///
+    /// To get a good understanding of what is happening here, check
+    /// out the [chapter in the rustc dev guide][c].
+    ///
+    /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query
+    pub fn canonicalize_query<V>(
+        &self,
+        value: V,
+        query_state: &mut OriginalQueryValues<'tcx>,
+    ) -> Canonicalized<'tcx, V>
+    where
+        V: TypeFoldable<'tcx>,
+    {
+        self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
+
+        Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
+    }
+
+    /// Canonicalizes a query *response* `V`. When we canonicalize a
+    /// query response, we only canonicalize unbound inference
+    /// variables, and we leave other free regions alone. So,
+    /// continuing with the example from `canonicalize_query`, if
+    /// there was an input query `T: Trait<'static>`, it would have
+    /// been canonicalized to
+    ///
+    /// ```text
+    /// T: Trait<'?0>
+    /// ```
+    ///
+    /// with a mapping M that maps `'?0` to `'static`. But if we found that there
+    /// exists only one possible impl of `Trait`, and it looks like
+    ///
+    ///     impl<T> Trait<'static> for T { .. }
+    ///
+    /// then we would prepare a query result R that (among other
+    /// things) includes a mapping to `'?0 := 'static`. When
+    /// canonicalizing this query result R, we would leave this
+    /// reference to `'static` alone.
+    ///
+    /// To get a good understanding of what is happening here, check
+    /// out the [chapter in the rustc dev guide][c].
+    ///
+    /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result
+    pub fn canonicalize_response<V>(&self, value: V) -> Canonicalized<'tcx, V>
+    where
+        V: TypeFoldable<'tcx>,
+    {
+        let mut query_state = OriginalQueryValues::default();
+        Canonicalizer::canonicalize(
+            value,
+            self,
+            self.tcx,
+            &CanonicalizeQueryResponse,
+            &mut query_state,
+        )
+    }
+
+    pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonicalized<'tcx, V>
+    where
+        V: TypeFoldable<'tcx>,
+    {
+        let mut query_state = OriginalQueryValues::default();
+        Canonicalizer::canonicalize(
+            value,
+            self,
+            self.tcx,
+            &CanonicalizeUserTypeAnnotation,
+            &mut query_state,
+        )
+    }
+
+    /// A variant of `canonicalize_query` that does not
+    /// canonicalize `'static`. This is useful when
+    /// the query implementation can perform more efficient
+    /// handling of `'static` regions (e.g. trait evaluation).
+    pub fn canonicalize_query_keep_static<V>(
+        &self,
+        value: V,
+        query_state: &mut OriginalQueryValues<'tcx>,
+    ) -> Canonicalized<'tcx, V>
+    where
+        V: TypeFoldable<'tcx>,
+    {
+        self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
+
+        Canonicalizer::canonicalize(
+            value,
+            self,
+            self.tcx,
+            &CanonicalizeFreeRegionsOtherThanStatic,
+            query_state,
+        )
+    }
+}
+
+/// Controls how we canonicalize "free regions" that are not inference
+/// variables. This depends on what we are canonicalizing *for* --
+/// e.g., if we are canonicalizing to create a query, we want to
+/// replace those with inference variables, since we want to make a
+/// maximally general query. But if we are canonicalizing a *query
+/// response*, then we don't typically replace free regions, as they
+/// must have been introduced from other parts of the system.
+trait CanonicalizeRegionMode {
+    fn canonicalize_free_region(
+        &self,
+        canonicalizer: &mut Canonicalizer<'_, 'tcx>,
+        r: ty::Region<'tcx>,
+    ) -> ty::Region<'tcx>;
+
+    fn any(&self) -> bool;
+}
+
+struct CanonicalizeQueryResponse;
+
+impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
+    fn canonicalize_free_region(
+        &self,
+        canonicalizer: &mut Canonicalizer<'_, 'tcx>,
+        r: ty::Region<'tcx>,
+    ) -> ty::Region<'tcx> {
+        match r {
+            ty::ReFree(_)
+            | ty::ReErased
+            | ty::ReStatic
+            | ty::ReEmpty(ty::UniverseIndex::ROOT)
+            | ty::ReEarlyBound(..) => r,
+
+            ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
+                CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder) },
+                r,
+            ),
+
+            ty::ReVar(vid) => {
+                let universe = canonicalizer.region_var_universe(*vid);
+                canonicalizer.canonical_var_for_region(
+                    CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
+                    r,
+                )
+            }
+
+            ty::ReEmpty(ui) => {
+                bug!("canonicalizing 'empty in universe {:?}", ui) // FIXME
+            }
+
+            _ => {
+                // Other than `'static` or `'empty`, the query
+                // response should be executing in a fully
+                // canonicalized environment, so there shouldn't be
+                // any other region names it can come up.
+                //
+                // rust-lang/rust#57464: `impl Trait` can leak local
+                // scopes (in manner violating typeck). Therefore, use
+                // `delay_span_bug` to allow type error over an ICE.
+                ty::tls::with(|tcx| {
+                    tcx.sess.delay_span_bug(
+                        rustc_span::DUMMY_SP,
+                        &format!("unexpected region in query response: `{:?}`", r),
+                    );
+                });
+                r
+            }
+        }
+    }
+
+    fn any(&self) -> bool {
+        false
+    }
+}
+
+struct CanonicalizeUserTypeAnnotation;
+
+impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
+    fn canonicalize_free_region(
+        &self,
+        canonicalizer: &mut Canonicalizer<'_, 'tcx>,
+        r: ty::Region<'tcx>,
+    ) -> ty::Region<'tcx> {
+        match r {
+            ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r,
+            ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
+            _ => {
+                // We only expect region names that the user can type.
+                bug!("unexpected region in query response: `{:?}`", r)
+            }
+        }
+    }
+
+    fn any(&self) -> bool {
+        false
+    }
+}
+
+struct CanonicalizeAllFreeRegions;
+
+impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions {
+    fn canonicalize_free_region(
+        &self,
+        canonicalizer: &mut Canonicalizer<'_, 'tcx>,
+        r: ty::Region<'tcx>,
+    ) -> ty::Region<'tcx> {
+        canonicalizer.canonical_var_for_region_in_root_universe(r)
+    }
+
+    fn any(&self) -> bool {
+        true
+    }
+}
+
+struct CanonicalizeFreeRegionsOtherThanStatic;
+
+impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
+    fn canonicalize_free_region(
+        &self,
+        canonicalizer: &mut Canonicalizer<'_, 'tcx>,
+        r: ty::Region<'tcx>,
+    ) -> ty::Region<'tcx> {
+        if let ty::ReStatic = r {
+            r
+        } else {
+            canonicalizer.canonical_var_for_region_in_root_universe(r)
+        }
+    }
+
+    fn any(&self) -> bool {
+        true
+    }
+}
+
+struct Canonicalizer<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'cx, 'tcx>,
+    tcx: TyCtxt<'tcx>,
+    variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
+    query_state: &'cx mut OriginalQueryValues<'tcx>,
+    // Note that indices is only used once `var_values` is big enough to be
+    // heap-allocated.
+    indices: FxHashMap<GenericArg<'tcx>, BoundVar>,
+    canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode,
+    needs_canonical_flags: TypeFlags,
+
+    binder_index: ty::DebruijnIndex,
+}
+
+impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.binder_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.binder_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReLateBound(index, ..) => {
+                if index >= self.binder_index {
+                    bug!("escaping late-bound region during canonicalization");
+                } else {
+                    r
+                }
+            }
+
+            ty::ReVar(vid) => {
+                let resolved_vid = self
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .unwrap_region_constraints()
+                    .opportunistic_resolve_var(vid);
+                debug!(
+                    "canonical: region var found with vid {:?}, \
+                     opportunistically resolved to {:?}",
+                    vid, r
+                );
+                let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid));
+                self.canonicalize_region_mode.canonicalize_free_region(self, r)
+            }
+
+            ty::ReStatic
+            | ty::ReEarlyBound(..)
+            | ty::ReFree(_)
+            | ty::ReEmpty(_)
+            | ty::RePlaceholder(..)
+            | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r),
+        }
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match *t.kind() {
+            ty::Infer(ty::TyVar(vid)) => {
+                debug!("canonical: type var found with vid {:?}", vid);
+                match self.infcx.probe_ty_var(vid) {
+                    // `t` could be a float / int variable; canonicalize that instead.
+                    Ok(t) => {
+                        debug!("(resolved to {:?})", t);
+                        self.fold_ty(t)
+                    }
+
+                    // `TyVar(vid)` is unresolved, track its universe index in the canonicalized
+                    // result.
+                    Err(mut ui) => {
+                        // FIXME: perf problem described in #55921.
+                        ui = ty::UniverseIndex::ROOT;
+                        self.canonicalize_ty_var(
+                            CanonicalVarInfo {
+                                kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
+                            },
+                            t,
+                        )
+                    }
+                }
+            }
+
+            ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(
+                CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) },
+                t,
+            ),
+
+            ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(
+                CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) },
+                t,
+            ),
+
+            ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                bug!("encountered a fresh type during canonicalization")
+            }
+
+            ty::Placeholder(placeholder) => self.canonicalize_ty_var(
+                CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) },
+                t,
+            ),
+
+            ty::Bound(debruijn, _) => {
+                if debruijn >= self.binder_index {
+                    bug!("escaping bound type during canonicalization")
+                } else {
+                    t
+                }
+            }
+
+            ty::Closure(..)
+            | ty::Generator(..)
+            | ty::GeneratorWitness(..)
+            | ty::Bool
+            | ty::Char
+            | ty::Int(..)
+            | ty::Uint(..)
+            | ty::Float(..)
+            | ty::Adt(..)
+            | ty::Str
+            | ty::Error(_)
+            | ty::Array(..)
+            | ty::Slice(..)
+            | ty::RawPtr(..)
+            | ty::Ref(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Dynamic(..)
+            | ty::Never
+            | ty::Tuple(..)
+            | ty::Projection(..)
+            | ty::Foreign(..)
+            | ty::Param(..)
+            | ty::Opaque(..) => {
+                if t.flags().intersects(self.needs_canonical_flags) {
+                    t.super_fold_with(self)
+                } else {
+                    t
+                }
+            }
+        }
+    }
+
+    fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        match ct.val {
+            ty::ConstKind::Infer(InferConst::Var(vid)) => {
+                debug!("canonical: const var found with vid {:?}", vid);
+                match self.infcx.probe_const_var(vid) {
+                    Ok(c) => {
+                        debug!("(resolved to {:?})", c);
+                        return self.fold_const(c);
+                    }
+
+                    // `ConstVar(vid)` is unresolved, track its universe index in the
+                    // canonicalized result
+                    Err(mut ui) => {
+                        // FIXME: perf problem described in #55921.
+                        ui = ty::UniverseIndex::ROOT;
+                        return self.canonicalize_const_var(
+                            CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) },
+                            ct,
+                        );
+                    }
+                }
+            }
+            ty::ConstKind::Infer(InferConst::Fresh(_)) => {
+                bug!("encountered a fresh const during canonicalization")
+            }
+            ty::ConstKind::Bound(debruijn, _) => {
+                if debruijn >= self.binder_index {
+                    bug!("escaping bound type during canonicalization")
+                } else {
+                    return ct;
+                }
+            }
+            ty::ConstKind::Placeholder(placeholder) => {
+                return self.canonicalize_const_var(
+                    CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderConst(placeholder) },
+                    ct,
+                );
+            }
+            _ => {}
+        }
+
+        let flags = FlagComputation::for_const(ct);
+        if flags.intersects(self.needs_canonical_flags) { ct.super_fold_with(self) } else { ct }
+    }
+}
+
+impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
+    /// The main `canonicalize` method, shared impl of
+    /// `canonicalize_query` and `canonicalize_response`.
+    fn canonicalize<V>(
+        value: V,
+        infcx: &InferCtxt<'_, 'tcx>,
+        tcx: TyCtxt<'tcx>,
+        canonicalize_region_mode: &dyn CanonicalizeRegionMode,
+        query_state: &mut OriginalQueryValues<'tcx>,
+    ) -> Canonicalized<'tcx, V>
+    where
+        V: TypeFoldable<'tcx>,
+    {
+        let needs_canonical_flags = if canonicalize_region_mode.any() {
+            TypeFlags::NEEDS_INFER |
+            TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS`
+            TypeFlags::HAS_TY_PLACEHOLDER |
+            TypeFlags::HAS_CT_PLACEHOLDER
+        } else {
+            TypeFlags::NEEDS_INFER
+                | TypeFlags::HAS_RE_PLACEHOLDER
+                | TypeFlags::HAS_TY_PLACEHOLDER
+                | TypeFlags::HAS_CT_PLACEHOLDER
+        };
+
+        // Fast path: nothing that needs to be canonicalized.
+        if !value.has_type_flags(needs_canonical_flags) {
+            let canon_value = Canonical {
+                max_universe: ty::UniverseIndex::ROOT,
+                variables: List::empty(),
+                value,
+            };
+            return canon_value;
+        }
+
+        let mut canonicalizer = Canonicalizer {
+            infcx,
+            tcx,
+            canonicalize_region_mode,
+            needs_canonical_flags,
+            variables: SmallVec::new(),
+            query_state,
+            indices: FxHashMap::default(),
+            binder_index: ty::INNERMOST,
+        };
+        let out_value = value.fold_with(&mut canonicalizer);
+
+        // Once we have canonicalized `out_value`, it should not
+        // contain anything that ties it to this inference context
+        // anymore, so it should live in the global arena.
+        debug_assert!(!out_value.needs_infer());
+
+        let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);
+
+        let max_universe = canonical_variables
+            .iter()
+            .map(|cvar| cvar.universe())
+            .max()
+            .unwrap_or(ty::UniverseIndex::ROOT);
+
+        Canonical { max_universe, variables: canonical_variables, value: out_value }
+    }
+
+    /// Creates a canonical variable replacing `kind` from the input,
+    /// or returns an existing variable if `kind` has already been
+    /// seen. `kind` is expected to be an unbound variable (or
+    /// potentially a free region).
+    fn canonical_var(&mut self, info: CanonicalVarInfo<'tcx>, kind: GenericArg<'tcx>) -> BoundVar {
+        let Canonicalizer { variables, query_state, indices, .. } = self;
+
+        let var_values = &mut query_state.var_values;
+
+        // This code is hot. `variables` and `var_values` are usually small
+        // (fewer than 8 elements ~95% of the time). They are SmallVec's to
+        // avoid allocations in those cases. We also don't use `indices` to
+        // determine if a kind has been seen before until the limit of 8 has
+        // been exceeded, to also avoid allocations for `indices`.
+        if !var_values.spilled() {
+            // `var_values` is stack-allocated. `indices` isn't used yet. Do a
+            // direct linear search of `var_values`.
+            if let Some(idx) = var_values.iter().position(|&k| k == kind) {
+                // `kind` is already present in `var_values`.
+                BoundVar::new(idx)
+            } else {
+                // `kind` isn't present in `var_values`. Append it. Likewise
+                // for `info` and `variables`.
+                variables.push(info);
+                var_values.push(kind);
+                assert_eq!(variables.len(), var_values.len());
+
+                // If `var_values` has become big enough to be heap-allocated,
+                // fill up `indices` to facilitate subsequent lookups.
+                if var_values.spilled() {
+                    assert!(indices.is_empty());
+                    *indices = var_values
+                        .iter()
+                        .enumerate()
+                        .map(|(i, &kind)| (kind, BoundVar::new(i)))
+                        .collect();
+                }
+                // The cv is the index of the appended element.
+                BoundVar::new(var_values.len() - 1)
+            }
+        } else {
+            // `var_values` is large. Do a hashmap search via `indices`.
+            *indices.entry(kind).or_insert_with(|| {
+                variables.push(info);
+                var_values.push(kind);
+                assert_eq!(variables.len(), var_values.len());
+                BoundVar::new(variables.len() - 1)
+            })
+        }
+    }
+
+    /// Shorthand helper that creates a canonical region variable for
+    /// `r` (always in the root universe). The reason that we always
+    /// put these variables into the root universe is because this
+    /// method is used during **query construction:** in that case, we
+    /// are taking all the regions and just putting them into the most
+    /// generic context we can. This may generate solutions that don't
+    /// fit (e.g., that equate some region variable with a placeholder
+    /// it can't name) on the caller side, but that's ok, the caller
+    /// can figure that out. In the meantime, it maximizes our
+    /// caching.
+    ///
+    /// (This works because unification never fails -- and hence trait
+    /// selection is never affected -- due to a universe mismatch.)
+    fn canonical_var_for_region_in_root_universe(
+        &mut self,
+        r: ty::Region<'tcx>,
+    ) -> ty::Region<'tcx> {
+        self.canonical_var_for_region(
+            CanonicalVarInfo { kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT) },
+            r,
+        )
+    }
+
+    /// Returns the universe in which `vid` is defined.
+    fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
+        self.infcx.inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
+    }
+
+    /// Creates a canonical variable (with the given `info`)
+    /// representing the region `r`; return a region referencing it.
+    fn canonical_var_for_region(
+        &mut self,
+        info: CanonicalVarInfo<'tcx>,
+        r: ty::Region<'tcx>,
+    ) -> ty::Region<'tcx> {
+        let var = self.canonical_var(info, r.into());
+        let br = ty::BoundRegion { var, kind: ty::BrAnon(var.as_u32()) };
+        let region = ty::ReLateBound(self.binder_index, br);
+        self.tcx().mk_region(region)
+    }
+
+    /// Given a type variable `ty_var` of the given kind, first check
+    /// if `ty_var` is bound to anything; if so, canonicalize
+    /// *that*. Otherwise, create a new canonical variable for
+    /// `ty_var`.
+    fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
+        let infcx = self.infcx;
+        let bound_to = infcx.shallow_resolve(ty_var);
+        if bound_to != ty_var {
+            self.fold_ty(bound_to)
+        } else {
+            let var = self.canonical_var(info, ty_var.into());
+            self.tcx().mk_ty(ty::Bound(self.binder_index, var.into()))
+        }
+    }
+
+    /// Given a type variable `const_var` of the given kind, first check
+    /// if `const_var` is bound to anything; if so, canonicalize
+    /// *that*. Otherwise, create a new canonical variable for
+    /// `const_var`.
+    fn canonicalize_const_var(
+        &mut self,
+        info: CanonicalVarInfo<'tcx>,
+        const_var: &'tcx ty::Const<'tcx>,
+    ) -> &'tcx ty::Const<'tcx> {
+        let infcx = self.infcx;
+        let bound_to = infcx.shallow_resolve(const_var);
+        if bound_to != const_var {
+            self.fold_const(bound_to)
+        } else {
+            let var = self.canonical_var(info, const_var.into());
+            self.tcx().mk_const(ty::Const {
+                val: ty::ConstKind::Bound(self.binder_index, var),
+                ty: self.fold_ty(const_var.ty),
+            })
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs
new file mode 100644
index 00000000000..0c26639e9b0
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/canonical/mod.rs
@@ -0,0 +1,163 @@
+//! **Canonicalization** is the key to constructing a query in the
+//! middle of type inference. Ordinarily, it is not possible to store
+//! types from type inference in query keys, because they contain
+//! references to inference variables whose lifetimes are too short
+//! and so forth. Canonicalizing a value T1 using `canonicalize_query`
+//! produces two things:
+//!
+//! - a value T2 where each unbound inference variable has been
+//!   replaced with a **canonical variable**;
+//! - a map M (of type `CanonicalVarValues`) from those canonical
+//!   variables back to the original.
+//!
+//! We can then do queries using T2. These will give back constraints
+//! on the canonical variables which can be translated, using the map
+//! M, into constraints in our source context. This process of
+//! translating the results back is done by the
+//! `instantiate_query_result` method.
+//!
+//! For a more detailed look at what is happening here, check
+//! out the [chapter in the rustc dev guide][c].
+//!
+//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
+
+use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind};
+use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_index::vec::IndexVec;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::subst::GenericArg;
+use rustc_middle::ty::{self, BoundVar, List};
+use rustc_span::source_map::Span;
+
+pub use rustc_middle::infer::canonical::*;
+use substitute::CanonicalExt;
+
+mod canonicalizer;
+pub mod query_response;
+mod substitute;
+
+impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
+    /// Creates a substitution S for the canonical value with fresh
+    /// inference variables and applies it to the canonical value.
+    /// Returns both the instantiated result *and* the substitution S.
+    ///
+    /// This is only meant to be invoked as part of constructing an
+    /// inference context at the start of a query (see
+    /// `InferCtxtBuilder::enter_with_canonical`). It basically
+    /// brings the canonical value "into scope" within your new infcx.
+    ///
+    /// At the end of processing, the substitution S (once
+    /// canonicalized) then represents the values that you computed
+    /// for each of the canonical inputs to your query.
+
+    pub fn instantiate_canonical_with_fresh_inference_vars<T>(
+        &self,
+        span: Span,
+        canonical: &Canonical<'tcx, T>,
+    ) -> (T, CanonicalVarValues<'tcx>)
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        // For each universe that is referred to in the incoming
+        // query, create a universe in our local inference context. In
+        // practice, as of this writing, all queries have no universes
+        // in them, so this code has no effect, but it is looking
+        // forward to the day when we *do* want to carry universes
+        // through into queries.
+        let universes: IndexVec<ty::UniverseIndex, _> = std::iter::once(ty::UniverseIndex::ROOT)
+            .chain((0..canonical.max_universe.as_u32()).map(|_| self.create_next_universe()))
+            .collect();
+
+        let canonical_inference_vars =
+            self.instantiate_canonical_vars(span, canonical.variables, |ui| universes[ui]);
+        let result = canonical.substitute(self.tcx, &canonical_inference_vars);
+        (result, canonical_inference_vars)
+    }
+
+    /// Given the "infos" about the canonical variables from some
+    /// canonical, creates fresh variables with the same
+    /// characteristics (see `instantiate_canonical_var` for
+    /// details). You can then use `substitute` to instantiate the
+    /// canonical variable with these inference variables.
+    fn instantiate_canonical_vars(
+        &self,
+        span: Span,
+        variables: &List<CanonicalVarInfo<'tcx>>,
+        universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
+    ) -> CanonicalVarValues<'tcx> {
+        let var_values: IndexVec<BoundVar, GenericArg<'tcx>> = variables
+            .iter()
+            .map(|info| self.instantiate_canonical_var(span, info, &universe_map))
+            .collect();
+
+        CanonicalVarValues { var_values }
+    }
+
+    /// Given the "info" about a canonical variable, creates a fresh
+    /// variable for it. If this is an existentially quantified
+    /// variable, then you'll get a new inference variable; if it is a
+    /// universally quantified variable, you get a placeholder.
+    fn instantiate_canonical_var(
+        &self,
+        span: Span,
+        cv_info: CanonicalVarInfo<'tcx>,
+        universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
+    ) -> GenericArg<'tcx> {
+        match cv_info.kind {
+            CanonicalVarKind::Ty(ty_kind) => {
+                let ty = match ty_kind {
+                    CanonicalTyVarKind::General(ui) => self.next_ty_var_in_universe(
+                        TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span },
+                        universe_map(ui),
+                    ),
+
+                    CanonicalTyVarKind::Int => self.next_int_var(),
+
+                    CanonicalTyVarKind::Float => self.next_float_var(),
+                };
+                ty.into()
+            }
+
+            CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, name }) => {
+                let universe_mapped = universe_map(universe);
+                let placeholder_mapped = ty::PlaceholderType { universe: universe_mapped, name };
+                self.tcx.mk_ty(ty::Placeholder(placeholder_mapped)).into()
+            }
+
+            CanonicalVarKind::Region(ui) => self
+                .next_region_var_in_universe(
+                    RegionVariableOrigin::MiscVariable(span),
+                    universe_map(ui),
+                )
+                .into(),
+
+            CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion { universe, name }) => {
+                let universe_mapped = universe_map(universe);
+                let placeholder_mapped = ty::PlaceholderRegion { universe: universe_mapped, name };
+                self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into()
+            }
+
+            CanonicalVarKind::Const(ui) => self
+                .next_const_var_in_universe(
+                    self.next_ty_var_in_universe(
+                        TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span },
+                        universe_map(ui),
+                    ),
+                    ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span },
+                    universe_map(ui),
+                )
+                .into(),
+
+            CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, name }) => {
+                let universe_mapped = universe_map(universe);
+                let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, name };
+                self.tcx
+                    .mk_const(ty::Const {
+                        val: ty::ConstKind::Placeholder(placeholder_mapped),
+                        ty: name.ty,
+                    })
+                    .into()
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
new file mode 100644
index 00000000000..c3c28d70081
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -0,0 +1,692 @@
+//! This module contains the code to instantiate a "query result", and
+//! in particular to extract out the resulting region obligations and
+//! encode them therein.
+//!
+//! For an overview of what canonicaliation is and how it fits into
+//! rustc, check out the [chapter in the rustc dev guide][c].
+//!
+//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
+
+use crate::infer::canonical::substitute::{substitute_value, CanonicalExt};
+use crate::infer::canonical::{
+    Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty, OriginalQueryValues,
+    QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse,
+};
+use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate};
+use crate::infer::region_constraints::{Constraint, RegionConstraintData};
+use crate::infer::{InferCtxt, InferOk, InferResult, NllRegionVariableOrigin};
+use crate::traits::query::{Fallible, NoSolution};
+use crate::traits::TraitEngine;
+use crate::traits::{Obligation, ObligationCause, PredicateObligation};
+use rustc_data_structures::captures::Captures;
+use rustc_index::vec::Idx;
+use rustc_index::vec::IndexVec;
+use rustc_middle::arena::ArenaAllocatable;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::relate::TypeRelation;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, BoundVar, Const, ToPredicate, Ty, TyCtxt};
+use std::fmt::Debug;
+use std::iter;
+
+impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
+    /// This method is meant to be invoked as the final step of a canonical query
+    /// implementation. It is given:
+    ///
+    /// - the instantiated variables `inference_vars` created from the query key
+    /// - the result `answer` of the query
+    /// - a fulfillment context `fulfill_cx` that may contain various obligations which
+    ///   have yet to be proven.
+    ///
+    /// Given this, the function will process the obligations pending
+    /// in `fulfill_cx`:
+    ///
+    /// - If all the obligations can be proven successfully, it will
+    ///   package up any resulting region obligations (extracted from
+    ///   `infcx`) along with the fully resolved value `answer` into a
+    ///   query result (which is then itself canonicalized).
+    /// - If some obligations can be neither proven nor disproven, then
+    ///   the same thing happens, but the resulting query is marked as ambiguous.
+    /// - Finally, if any of the obligations result in a hard error,
+    ///   then `Err(NoSolution)` is returned.
+    pub fn make_canonicalized_query_response<T>(
+        &self,
+        inference_vars: CanonicalVarValues<'tcx>,
+        answer: T,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Fallible<CanonicalizedQueryResponse<'tcx, T>>
+    where
+        T: Debug + TypeFoldable<'tcx>,
+        Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
+    {
+        let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?;
+        let canonical_result = self.canonicalize_response(query_response);
+
+        debug!("make_canonicalized_query_response: canonical_result = {:#?}", canonical_result);
+
+        Ok(self.tcx.arena.alloc(canonical_result))
+    }
+
+    /// A version of `make_canonicalized_query_response` that does
+    /// not pack in obligations, for contexts that want to drop
+    /// pending obligations instead of treating them as an ambiguity (e.g.
+    /// typeck "probing" contexts).
+    ///
+    /// If you DO want to keep track of pending obligations (which
+    /// include all region obligations, so this includes all cases
+    /// that care about regions) with this function, you have to
+    /// do it yourself, by e.g., having them be a part of the answer.
+    pub fn make_query_response_ignoring_pending_obligations<T>(
+        &self,
+        inference_vars: CanonicalVarValues<'tcx>,
+        answer: T,
+    ) -> Canonical<'tcx, QueryResponse<'tcx, T>>
+    where
+        T: Debug + TypeFoldable<'tcx>,
+    {
+        self.canonicalize_response(QueryResponse {
+            var_values: inference_vars,
+            region_constraints: QueryRegionConstraints::default(),
+            certainty: Certainty::Proven, // Ambiguities are OK!
+            value: answer,
+        })
+    }
+
+    /// Helper for `make_canonicalized_query_response` that does
+    /// everything up until the final canonicalization.
+    fn make_query_response<T>(
+        &self,
+        inference_vars: CanonicalVarValues<'tcx>,
+        answer: T,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<QueryResponse<'tcx, T>, NoSolution>
+    where
+        T: Debug + TypeFoldable<'tcx>,
+    {
+        let tcx = self.tcx;
+
+        debug!(
+            "make_query_response(\
+             inference_vars={:?}, \
+             answer={:?})",
+            inference_vars, answer,
+        );
+
+        // Select everything, returning errors.
+        let true_errors = fulfill_cx.select_where_possible(self).err().unwrap_or_else(Vec::new);
+        debug!("true_errors = {:#?}", true_errors);
+
+        if !true_errors.is_empty() {
+            // FIXME -- we don't indicate *why* we failed to solve
+            debug!("make_query_response: true_errors={:#?}", true_errors);
+            return Err(NoSolution);
+        }
+
+        // Anything left unselected *now* must be an ambiguity.
+        let ambig_errors = fulfill_cx.select_all_or_error(self).err().unwrap_or_else(Vec::new);
+        debug!("ambig_errors = {:#?}", ambig_errors);
+
+        let region_obligations = self.take_registered_region_obligations();
+        let region_constraints = self.with_region_constraints(|region_constraints| {
+            make_query_region_constraints(
+                tcx,
+                region_obligations.iter().map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)),
+                region_constraints,
+            )
+        });
+
+        let certainty =
+            if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous };
+
+        Ok(QueryResponse {
+            var_values: inference_vars,
+            region_constraints,
+            certainty,
+            value: answer,
+        })
+    }
+
+    /// Given the (canonicalized) result to a canonical query,
+    /// instantiates the result so it can be used, plugging in the
+    /// values from the canonical query. (Note that the result may
+    /// have been ambiguous; you should check the certainty level of
+    /// the query before applying this function.)
+    ///
+    /// To get a good understanding of what is happening here, check
+    /// out the [chapter in the rustc dev guide][c].
+    ///
+    /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#processing-the-canonicalized-query-result
+    pub fn instantiate_query_response_and_region_obligations<R>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: &OriginalQueryValues<'tcx>,
+        query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
+    ) -> InferResult<'tcx, R>
+    where
+        R: Debug + TypeFoldable<'tcx>,
+    {
+        let InferOk { value: result_subst, mut obligations } =
+            self.query_response_substitution(cause, param_env, original_values, query_response)?;
+
+        obligations.extend(self.query_outlives_constraints_into_obligations(
+            cause,
+            param_env,
+            &query_response.value.region_constraints.outlives,
+            &result_subst,
+        ));
+
+        let user_result: R =
+            query_response.substitute_projected(self.tcx, &result_subst, |q_r| q_r.value.clone());
+
+        Ok(InferOk { value: user_result, obligations })
+    }
+
+    /// An alternative to
+    /// `instantiate_query_response_and_region_obligations` that is more
+    /// efficient for NLL. NLL is a bit more advanced in the
+    /// "transition to chalk" than the rest of the compiler. During
+    /// the NLL type check, all of the "processing" of types and
+    /// things happens in queries -- the NLL checker itself is only
+    /// interested in the region obligations (`'a: 'b` or `T: 'b`)
+    /// that come out of these queries, which it wants to convert into
+    /// MIR-based constraints and solve. Therefore, it is most
+    /// convenient for the NLL Type Checker to **directly consume**
+    /// the `QueryOutlivesConstraint` values that arise from doing a
+    /// query. This is contrast to other parts of the compiler, which
+    /// would prefer for those `QueryOutlivesConstraint` to be converted
+    /// into the older infcx-style constraints (e.g., calls to
+    /// `sub_regions` or `register_region_obligation`).
+    ///
+    /// Therefore, `instantiate_nll_query_response_and_region_obligations` performs the same
+    /// basic operations as `instantiate_query_response_and_region_obligations` but
+    /// it returns its result differently:
+    ///
+    /// - It creates a substitution `S` that maps from the original
+    ///   query variables to the values computed in the query
+    ///   result. If any errors arise, they are propagated back as an
+    ///   `Err` result.
+    /// - In the case of a successful substitution, we will append
+    ///   `QueryOutlivesConstraint` values onto the
+    ///   `output_query_region_constraints` vector for the solver to
+    ///   use (if an error arises, some values may also be pushed, but
+    ///   they should be ignored).
+    /// - It **can happen** (though it rarely does currently) that
+    ///   equating types and things will give rise to subobligations
+    ///   that must be processed. In this case, those subobligations
+    ///   are propagated back in the return value.
+    /// - Finally, the query result (of type `R`) is propagated back,
+    ///   after applying the substitution `S`.
+    pub fn instantiate_nll_query_response_and_region_obligations<R>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: &OriginalQueryValues<'tcx>,
+        query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
+        output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
+    ) -> InferResult<'tcx, R>
+    where
+        R: Debug + TypeFoldable<'tcx>,
+    {
+        let result_subst =
+            self.query_response_substitution_guess(cause, original_values, query_response);
+
+        // Compute `QueryOutlivesConstraint` values that unify each of
+        // the original values `v_o` that was canonicalized into a
+        // variable...
+        let mut obligations = vec![];
+
+        for (index, original_value) in original_values.var_values.iter().enumerate() {
+            // ...with the value `v_r` of that variable from the query.
+            let result_value = query_response.substitute_projected(self.tcx, &result_subst, |v| {
+                v.var_values[BoundVar::new(index)]
+            });
+            match (original_value.unpack(), result_value.unpack()) {
+                (
+                    GenericArgKind::Lifetime(ty::ReErased),
+                    GenericArgKind::Lifetime(ty::ReErased),
+                ) => {
+                    // No action needed.
+                }
+
+                (GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => {
+                    // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
+                    if v_o != v_r {
+                        output_query_region_constraints
+                            .outlives
+                            .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)));
+                        output_query_region_constraints
+                            .outlives
+                            .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)));
+                    }
+                }
+
+                (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
+                    TypeRelating::new(
+                        self,
+                        QueryTypeRelatingDelegate {
+                            infcx: self,
+                            param_env,
+                            cause,
+                            obligations: &mut obligations,
+                        },
+                        ty::Variance::Invariant,
+                    )
+                    .relate(v1, v2)?;
+                }
+
+                (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
+                    TypeRelating::new(
+                        self,
+                        QueryTypeRelatingDelegate {
+                            infcx: self,
+                            param_env,
+                            cause,
+                            obligations: &mut obligations,
+                        },
+                        ty::Variance::Invariant,
+                    )
+                    .relate(v1, v2)?;
+                }
+
+                _ => {
+                    bug!("kind mismatch, cannot unify {:?} and {:?}", original_value, result_value);
+                }
+            }
+        }
+
+        // ...also include the other query region constraints from the query.
+        output_query_region_constraints.outlives.extend(
+            query_response.value.region_constraints.outlives.iter().filter_map(|&r_c| {
+                let r_c = substitute_value(self.tcx, &result_subst, r_c);
+
+                // Screen out `'a: 'a` cases -- we skip the binder here but
+                // only compare the inner values to one another, so they are still at
+                // consistent binding levels.
+                let ty::OutlivesPredicate(k1, r2) = r_c.skip_binder();
+                if k1 != r2.into() { Some(r_c) } else { None }
+            }),
+        );
+
+        // ...also include the query member constraints.
+        output_query_region_constraints.member_constraints.extend(
+            query_response
+                .value
+                .region_constraints
+                .member_constraints
+                .iter()
+                .map(|p_c| substitute_value(self.tcx, &result_subst, p_c.clone())),
+        );
+
+        let user_result: R =
+            query_response.substitute_projected(self.tcx, &result_subst, |q_r| q_r.value.clone());
+
+        Ok(InferOk { value: user_result, obligations })
+    }
+
+    /// Given the original values and the (canonicalized) result from
+    /// computing a query, returns a substitution that can be applied
+    /// to the query result to convert the result back into the
+    /// original namespace.
+    ///
+    /// The substitution also comes accompanied with subobligations
+    /// that arose from unification; these might occur if (for
+    /// example) we are doing lazy normalization and the value
+    /// assigned to a type variable is unified with an unnormalized
+    /// projection.
+    fn query_response_substitution<R>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: &OriginalQueryValues<'tcx>,
+        query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
+    ) -> InferResult<'tcx, CanonicalVarValues<'tcx>>
+    where
+        R: Debug + TypeFoldable<'tcx>,
+    {
+        debug!(
+            "query_response_substitution(original_values={:#?}, query_response={:#?})",
+            original_values, query_response,
+        );
+
+        let result_subst =
+            self.query_response_substitution_guess(cause, original_values, query_response);
+
+        let obligations = self
+            .unify_query_response_substitution_guess(
+                cause,
+                param_env,
+                original_values,
+                &result_subst,
+                query_response,
+            )?
+            .into_obligations();
+
+        Ok(InferOk { value: result_subst, obligations })
+    }
+
+    /// Given the original values and the (canonicalized) result from
+    /// computing a query, returns a **guess** at a substitution that
+    /// can be applied to the query result to convert the result back
+    /// into the original namespace. This is called a **guess**
+    /// because it uses a quick heuristic to find the values for each
+    /// canonical variable; if that quick heuristic fails, then we
+    /// will instantiate fresh inference variables for each canonical
+    /// variable instead. Therefore, the result of this method must be
+    /// properly unified
+    fn query_response_substitution_guess<R>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        original_values: &OriginalQueryValues<'tcx>,
+        query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
+    ) -> CanonicalVarValues<'tcx>
+    where
+        R: Debug + TypeFoldable<'tcx>,
+    {
+        debug!(
+            "query_response_substitution_guess(original_values={:#?}, query_response={:#?})",
+            original_values, query_response,
+        );
+
+        // For each new universe created in the query result that did
+        // not appear in the original query, create a local
+        // superuniverse.
+        let mut universe_map = original_values.universe_map.clone();
+        let num_universes_in_query = original_values.universe_map.len();
+        let num_universes_in_response = query_response.max_universe.as_usize() + 1;
+        for _ in num_universes_in_query..num_universes_in_response {
+            universe_map.push(self.create_next_universe());
+        }
+        assert!(!universe_map.is_empty()); // always have the root universe
+        assert_eq!(universe_map[ty::UniverseIndex::ROOT.as_usize()], ty::UniverseIndex::ROOT);
+
+        // Every canonical query result includes values for each of
+        // the inputs to the query. Therefore, we begin by unifying
+        // these values with the original inputs that were
+        // canonicalized.
+        let result_values = &query_response.value.var_values;
+        assert_eq!(original_values.var_values.len(), result_values.len());
+
+        // Quickly try to find initial values for the canonical
+        // variables in the result in terms of the query. We do this
+        // by iterating down the values that the query gave to each of
+        // the canonical inputs. If we find that one of those values
+        // is directly equal to one of the canonical variables in the
+        // result, then we can type the corresponding value from the
+        // input. See the example above.
+        let mut opt_values: IndexVec<BoundVar, Option<GenericArg<'tcx>>> =
+            IndexVec::from_elem_n(None, query_response.variables.len());
+
+        // In terms of our example above, we are iterating over pairs like:
+        // [(?A, Vec<?0>), ('static, '?1), (?B, ?0)]
+        for (original_value, result_value) in iter::zip(&original_values.var_values, result_values)
+        {
+            match result_value.unpack() {
+                GenericArgKind::Type(result_value) => {
+                    // e.g., here `result_value` might be `?0` in the example above...
+                    if let ty::Bound(debruijn, b) = *result_value.kind() {
+                        // ...in which case we would set `canonical_vars[0]` to `Some(?U)`.
+
+                        // We only allow a `ty::INNERMOST` index in substitutions.
+                        assert_eq!(debruijn, ty::INNERMOST);
+                        opt_values[b.var] = Some(*original_value);
+                    }
+                }
+                GenericArgKind::Lifetime(result_value) => {
+                    // e.g., here `result_value` might be `'?1` in the example above...
+                    if let &ty::RegionKind::ReLateBound(debruijn, br) = result_value {
+                        // ... in which case we would set `canonical_vars[0]` to `Some('static)`.
+
+                        // We only allow a `ty::INNERMOST` index in substitutions.
+                        assert_eq!(debruijn, ty::INNERMOST);
+                        opt_values[br.var] = Some(*original_value);
+                    }
+                }
+                GenericArgKind::Const(result_value) => {
+                    if let ty::Const { val: ty::ConstKind::Bound(debrujin, b), .. } = result_value {
+                        // ...in which case we would set `canonical_vars[0]` to `Some(const X)`.
+
+                        // We only allow a `ty::INNERMOST` index in substitutions.
+                        assert_eq!(*debrujin, ty::INNERMOST);
+                        opt_values[*b] = Some(*original_value);
+                    }
+                }
+            }
+        }
+
+        // Create a result substitution: if we found a value for a
+        // given variable in the loop above, use that. Otherwise, use
+        // a fresh inference variable.
+        let result_subst = CanonicalVarValues {
+            var_values: query_response
+                .variables
+                .iter()
+                .enumerate()
+                .map(|(index, info)| {
+                    if info.is_existential() {
+                        match opt_values[BoundVar::new(index)] {
+                            Some(k) => k,
+                            None => self.instantiate_canonical_var(cause.span, info, |u| {
+                                universe_map[u.as_usize()]
+                            }),
+                        }
+                    } else {
+                        self.instantiate_canonical_var(cause.span, info, |u| {
+                            universe_map[u.as_usize()]
+                        })
+                    }
+                })
+                .collect(),
+        };
+
+        result_subst
+    }
+
+    /// Given a "guess" at the values for the canonical variables in
+    /// the input, try to unify with the *actual* values found in the
+    /// query result. Often, but not always, this is a no-op, because
+    /// we already found the mapping in the "guessing" step.
+    ///
+    /// See also: `query_response_substitution_guess`
+    fn unify_query_response_substitution_guess<R>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: &OriginalQueryValues<'tcx>,
+        result_subst: &CanonicalVarValues<'tcx>,
+        query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
+    ) -> InferResult<'tcx, ()>
+    where
+        R: Debug + TypeFoldable<'tcx>,
+    {
+        // A closure that yields the result value for the given
+        // canonical variable; this is taken from
+        // `query_response.var_values` after applying the substitution
+        // `result_subst`.
+        let substituted_query_response = |index: BoundVar| -> GenericArg<'tcx> {
+            query_response.substitute_projected(self.tcx, &result_subst, |v| v.var_values[index])
+        };
+
+        // Unify the original value for each variable with the value
+        // taken from `query_response` (after applying `result_subst`).
+        self.unify_canonical_vars(cause, param_env, original_values, substituted_query_response)
+    }
+
+    /// Converts the region constraints resulting from a query into an
+    /// iterator of obligations.
+    fn query_outlives_constraints_into_obligations<'a>(
+        &'a self,
+        cause: &'a ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        unsubstituted_region_constraints: &'a [QueryOutlivesConstraint<'tcx>],
+        result_subst: &'a CanonicalVarValues<'tcx>,
+    ) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a + Captures<'tcx> {
+        unsubstituted_region_constraints.iter().map(move |&constraint| {
+            let predicate = substitute_value(self.tcx, result_subst, constraint);
+            let ty::OutlivesPredicate(k1, r2) = predicate.skip_binder();
+
+            let atom = match k1.unpack() {
+                GenericArgKind::Lifetime(r1) => {
+                    ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r1, r2))
+                }
+                GenericArgKind::Type(t1) => {
+                    ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(t1, r2))
+                }
+                GenericArgKind::Const(..) => {
+                    // Consts cannot outlive one another, so we don't expect to
+                    // encounter this branch.
+                    span_bug!(cause.span, "unexpected const outlives {:?}", constraint);
+                }
+            };
+            let predicate = predicate.rebind(atom).to_predicate(self.tcx);
+
+            Obligation::new(cause.clone(), param_env, predicate)
+        })
+    }
+
+    /// Given two sets of values for the same set of canonical variables, unify them.
+    /// The second set is produced lazily by supplying indices from the first set.
+    fn unify_canonical_vars(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        variables1: &OriginalQueryValues<'tcx>,
+        variables2: impl Fn(BoundVar) -> GenericArg<'tcx>,
+    ) -> InferResult<'tcx, ()> {
+        self.commit_if_ok(|_| {
+            let mut obligations = vec![];
+            for (index, value1) in variables1.var_values.iter().enumerate() {
+                let value2 = variables2(BoundVar::new(index));
+
+                match (value1.unpack(), value2.unpack()) {
+                    (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
+                        obligations
+                            .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations());
+                    }
+                    (
+                        GenericArgKind::Lifetime(ty::ReErased),
+                        GenericArgKind::Lifetime(ty::ReErased),
+                    ) => {
+                        // no action needed
+                    }
+                    (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => {
+                        obligations
+                            .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations());
+                    }
+                    (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
+                        let ok = self.at(cause, param_env).eq(v1, v2)?;
+                        obligations.extend(ok.into_obligations());
+                    }
+                    _ => {
+                        bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,);
+                    }
+                }
+            }
+            Ok(InferOk { value: (), obligations })
+        })
+    }
+}
+
+/// Given the region obligations and constraints scraped from the infcx,
+/// creates query region constraints.
+pub fn make_query_region_constraints<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>)>,
+    region_constraints: &RegionConstraintData<'tcx>,
+) -> QueryRegionConstraints<'tcx> {
+    let RegionConstraintData { constraints, verifys, givens, member_constraints } =
+        region_constraints;
+
+    assert!(verifys.is_empty());
+    assert!(givens.is_empty());
+
+    let outlives: Vec<_> = constraints
+        .iter()
+        .map(|(k, _)| match *k {
+            // Swap regions because we are going from sub (<=) to outlives
+            // (>=).
+            Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate(
+                tcx.mk_region(ty::ReVar(v2)).into(),
+                tcx.mk_region(ty::ReVar(v1)),
+            ),
+            Constraint::VarSubReg(v1, r2) => {
+                ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1)))
+            }
+            Constraint::RegSubVar(r1, v2) => {
+                ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1)
+            }
+            Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1),
+        })
+        .map(ty::Binder::dummy) // no bound vars in the code above
+        .chain(
+            outlives_obligations
+                .map(|(ty, r)| ty::OutlivesPredicate(ty.into(), r))
+                .map(ty::Binder::dummy), // no bound vars in the code above
+        )
+        .collect();
+
+    QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() }
+}
+
+struct QueryTypeRelatingDelegate<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: &'a ObligationCause<'tcx>,
+}
+
+impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.param_env
+    }
+
+    fn create_next_universe(&mut self) -> ty::UniverseIndex {
+        self.infcx.create_next_universe()
+    }
+
+    fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> {
+        let origin = NllRegionVariableOrigin::Existential { from_forall };
+        self.infcx.next_nll_region_var(origin)
+    }
+
+    fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> {
+        self.infcx.tcx.mk_region(ty::RePlaceholder(placeholder))
+    }
+
+    fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
+        self.infcx.next_nll_region_var_in_universe(
+            NllRegionVariableOrigin::Existential { from_forall: false },
+            universe,
+        )
+    }
+
+    fn push_outlives(
+        &mut self,
+        sup: ty::Region<'tcx>,
+        sub: ty::Region<'tcx>,
+        _info: ty::VarianceDiagInfo<'tcx>,
+    ) {
+        self.obligations.push(Obligation {
+            cause: self.cause.clone(),
+            param_env: self.param_env,
+            predicate: ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(sup, sub))
+                .to_predicate(self.infcx.tcx),
+            recursion_depth: 0,
+        });
+    }
+
+    fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {
+        span_bug!(
+            self.cause.span(self.infcx.tcx),
+            "lazy_normalization_consts: unreachable `const_equate`"
+        );
+    }
+
+    fn normalization() -> NormalizationStrategy {
+        NormalizationStrategy::Eager
+    }
+
+    fn forbid_inference_vars() -> bool {
+        true
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs
new file mode 100644
index 00000000000..553a11d4393
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs
@@ -0,0 +1,91 @@
+//! This module contains code to substitute new values into a
+//! `Canonical<'tcx, T>`.
+//!
+//! For an overview of what canonicalization is and how it fits into
+//! rustc, check out the [chapter in the rustc dev guide][c].
+//!
+//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
+
+use crate::infer::canonical::{Canonical, CanonicalVarValues};
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, TyCtxt};
+
+pub(super) trait CanonicalExt<'tcx, V> {
+    /// Instantiate the wrapped value, replacing each canonical value
+    /// with the value given in `var_values`.
+    fn substitute(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
+    where
+        V: TypeFoldable<'tcx>;
+
+    /// Allows one to apply a substitute to some subset of
+    /// `self.value`. Invoke `projection_fn` with `self.value` to get
+    /// a value V that is expressed in terms of the same canonical
+    /// variables bound in `self` (usually this extracts from subset
+    /// of `self`). Apply the substitution `var_values` to this value
+    /// V, replacing each of the canonical variables.
+    fn substitute_projected<T>(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        var_values: &CanonicalVarValues<'tcx>,
+        projection_fn: impl FnOnce(&V) -> T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>;
+}
+
+impl<'tcx, V> CanonicalExt<'tcx, V> for Canonical<'tcx, V> {
+    fn substitute(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
+    where
+        V: TypeFoldable<'tcx>,
+    {
+        self.substitute_projected(tcx, var_values, |value| value.clone())
+    }
+
+    fn substitute_projected<T>(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        var_values: &CanonicalVarValues<'tcx>,
+        projection_fn: impl FnOnce(&V) -> T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        assert_eq!(self.variables.len(), var_values.len());
+        let value = projection_fn(&self.value);
+        substitute_value(tcx, var_values, value)
+    }
+}
+
+/// Substitute the values from `var_values` into `value`. `var_values`
+/// must be values for the set of canonical variables that appear in
+/// `value`.
+pub(super) fn substitute_value<'tcx, T>(
+    tcx: TyCtxt<'tcx>,
+    var_values: &CanonicalVarValues<'tcx>,
+    value: T,
+) -> T
+where
+    T: TypeFoldable<'tcx>,
+{
+    if var_values.var_values.is_empty() {
+        value
+    } else {
+        let fld_r = |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() {
+            GenericArgKind::Lifetime(l) => l,
+            r => bug!("{:?} is a region but value is {:?}", br, r),
+        };
+
+        let fld_t = |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() {
+            GenericArgKind::Type(ty) => ty,
+            r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
+        };
+
+        let fld_c = |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() {
+            GenericArgKind::Const(ct) => ct,
+            c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
+        };
+
+        tcx.replace_escaping_bound_vars(value, fld_r, fld_t, fld_c)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
new file mode 100644
index 00000000000..3a11b5a2144
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -0,0 +1,992 @@
+///////////////////////////////////////////////////////////////////////////
+// # Type combining
+//
+// There are four type combiners: equate, sub, lub, and glb.  Each
+// implements the trait `Combine` and contains methods for combining
+// two instances of various things and yielding a new instance.  These
+// combiner methods always yield a `Result<T>`.  There is a lot of
+// common code for these operations, implemented as default methods on
+// the `Combine` trait.
+//
+// Each operation may have side-effects on the inference context,
+// though these can be unrolled using snapshots. On success, the
+// LUB/GLB operations return the appropriate bound. The Eq and Sub
+// operations generally return the first operand.
+//
+// ## Contravariance
+//
+// When you are relating two things which have a contravariant
+// relationship, you should use `contratys()` or `contraregions()`,
+// rather than inversing the order of arguments!  This is necessary
+// because the order of arguments is not relevant for LUB and GLB.  It
+// is also useful to track which value is the "expected" value in
+// terms of error reporting.
+
+use super::equate::Equate;
+use super::glb::Glb;
+use super::lub::Lub;
+use super::sub::Sub;
+use super::type_variable::TypeVariableValue;
+use super::unify_key::replace_if_possible;
+use super::unify_key::{ConstVarValue, ConstVariableValue};
+use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use super::{InferCtxt, MiscVariable, TypeTrace};
+
+use crate::traits::{Obligation, PredicateObligations};
+
+use rustc_data_structures::sso::SsoHashMap;
+use rustc_hir::def_id::DefId;
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{IntType, UintType};
+use rustc_span::{Span, DUMMY_SP};
+
+#[derive(Clone)]
+pub struct CombineFields<'infcx, 'tcx> {
+    pub infcx: &'infcx InferCtxt<'infcx, 'tcx>,
+    pub trace: TypeTrace<'tcx>,
+    pub cause: Option<ty::relate::Cause>,
+    pub param_env: ty::ParamEnv<'tcx>,
+    pub obligations: PredicateObligations<'tcx>,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum RelationDir {
+    SubtypeOf,
+    SupertypeOf,
+    EqTo,
+}
+
+impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
+    pub fn super_combine_tys<R>(
+        &self,
+        relation: &mut R,
+        a: Ty<'tcx>,
+        b: Ty<'tcx>,
+    ) -> RelateResult<'tcx, Ty<'tcx>>
+    where
+        R: TypeRelation<'tcx>,
+    {
+        let a_is_expected = relation.a_is_expected();
+
+        match (a.kind(), b.kind()) {
+            // Relate integral variables to other types
+            (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => {
+                self.inner
+                    .borrow_mut()
+                    .int_unification_table()
+                    .unify_var_var(a_id, b_id)
+                    .map_err(|e| int_unification_error(a_is_expected, e))?;
+                Ok(a)
+            }
+            (&ty::Infer(ty::IntVar(v_id)), &ty::Int(v)) => {
+                self.unify_integral_variable(a_is_expected, v_id, IntType(v))
+            }
+            (&ty::Int(v), &ty::Infer(ty::IntVar(v_id))) => {
+                self.unify_integral_variable(!a_is_expected, v_id, IntType(v))
+            }
+            (&ty::Infer(ty::IntVar(v_id)), &ty::Uint(v)) => {
+                self.unify_integral_variable(a_is_expected, v_id, UintType(v))
+            }
+            (&ty::Uint(v), &ty::Infer(ty::IntVar(v_id))) => {
+                self.unify_integral_variable(!a_is_expected, v_id, UintType(v))
+            }
+
+            // Relate floating-point variables to other types
+            (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => {
+                self.inner
+                    .borrow_mut()
+                    .float_unification_table()
+                    .unify_var_var(a_id, b_id)
+                    .map_err(|e| float_unification_error(relation.a_is_expected(), e))?;
+                Ok(a)
+            }
+            (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => {
+                self.unify_float_variable(a_is_expected, v_id, v)
+            }
+            (&ty::Float(v), &ty::Infer(ty::FloatVar(v_id))) => {
+                self.unify_float_variable(!a_is_expected, v_id, v)
+            }
+
+            // All other cases of inference are errors
+            (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
+                Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b)))
+            }
+
+            _ => ty::relate::super_relate_tys(relation, a, b),
+        }
+    }
+
+    pub fn super_combine_consts<R>(
+        &self,
+        relation: &mut R,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>
+    where
+        R: ConstEquateRelation<'tcx>,
+    {
+        debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
+        if a == b {
+            return Ok(a);
+        }
+
+        let a = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table(), a);
+        let b = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table(), b);
+
+        let a_is_expected = relation.a_is_expected();
+
+        match (a.val, b.val) {
+            (
+                ty::ConstKind::Infer(InferConst::Var(a_vid)),
+                ty::ConstKind::Infer(InferConst::Var(b_vid)),
+            ) => {
+                self.inner
+                    .borrow_mut()
+                    .const_unification_table()
+                    .unify_var_var(a_vid, b_vid)
+                    .map_err(|e| const_unification_error(a_is_expected, e))?;
+                return Ok(a);
+            }
+
+            // All other cases of inference with other variables are errors.
+            (ty::ConstKind::Infer(InferConst::Var(_)), ty::ConstKind::Infer(_))
+            | (ty::ConstKind::Infer(_), ty::ConstKind::Infer(InferConst::Var(_))) => {
+                bug!("tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var)")
+            }
+
+            (ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
+                return self.unify_const_variable(relation.param_env(), vid, b, a_is_expected);
+            }
+
+            (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
+                return self.unify_const_variable(relation.param_env(), vid, a, !a_is_expected);
+            }
+            (ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
+                // FIXME(#59490): Need to remove the leak check to accommodate
+                // escaping bound variables here.
+                if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
+                    relation.const_equate_obligation(a, b);
+                }
+                return Ok(b);
+            }
+            (_, ty::ConstKind::Unevaluated(..)) if self.tcx.lazy_normalization() => {
+                // FIXME(#59490): Need to remove the leak check to accommodate
+                // escaping bound variables here.
+                if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
+                    relation.const_equate_obligation(a, b);
+                }
+                return Ok(a);
+            }
+            _ => {}
+        }
+
+        ty::relate::super_relate_consts(relation, a, b)
+    }
+
+    /// Unifies the const variable `target_vid` with the given constant.
+    ///
+    /// This also tests if the given const `ct` contains an inference variable which was previously
+    /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
+    /// would result in an infinite type as we continuously replace an inference variable
+    /// in `ct` with `ct` itself.
+    ///
+    /// This is especially important as unevaluated consts use their parents generics.
+    /// They therefore often contain unused substs, making these errors far more likely.
+    ///
+    /// A good example of this is the following:
+    ///
+    /// ```rust
+    /// #![feature(const_generics)]
+    ///
+    /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
+    ///     todo!()
+    /// }
+    ///
+    /// fn main() {
+    ///     let mut arr = Default::default();
+    ///     arr = bind(arr);
+    /// }
+    /// ```
+    ///
+    /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics
+    /// of `fn bind` (meaning that its substs contain `N`).
+    ///
+    /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`.
+    /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`.
+    ///
+    /// As `3 + 4` contains `N` in its substs, this must not succeed.
+    ///
+    /// See `src/test/ui/const-generics/occurs-check/` for more examples where this is relevant.
+    #[instrument(level = "debug", skip(self))]
+    fn unify_const_variable(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        target_vid: ty::ConstVid<'tcx>,
+        ct: &'tcx ty::Const<'tcx>,
+        vid_is_expected: bool,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        let (for_universe, span) = {
+            let mut inner = self.inner.borrow_mut();
+            let variable_table = &mut inner.const_unification_table();
+            let var_value = variable_table.probe_value(target_vid);
+            match var_value.val {
+                ConstVariableValue::Known { value } => {
+                    bug!("instantiating {:?} which has a known value {:?}", target_vid, value)
+                }
+                ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span),
+            }
+        };
+        let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid }
+            .relate(ct, ct)?;
+
+        self.inner
+            .borrow_mut()
+            .const_unification_table()
+            .unify_var_value(
+                target_vid,
+                ConstVarValue {
+                    origin: ConstVariableOrigin {
+                        kind: ConstVariableOriginKind::ConstInference,
+                        span: DUMMY_SP,
+                    },
+                    val: ConstVariableValue::Known { value },
+                },
+            )
+            .map(|()| value)
+            .map_err(|e| const_unification_error(vid_is_expected, e))
+    }
+
+    fn unify_integral_variable(
+        &self,
+        vid_is_expected: bool,
+        vid: ty::IntVid,
+        val: ty::IntVarValue,
+    ) -> RelateResult<'tcx, Ty<'tcx>> {
+        self.inner
+            .borrow_mut()
+            .int_unification_table()
+            .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)),
+            UintType(v) => Ok(self.tcx.mk_mach_uint(v)),
+        }
+    }
+
+    fn unify_float_variable(
+        &self,
+        vid_is_expected: bool,
+        vid: ty::FloatVid,
+        val: ty::FloatTy,
+    ) -> RelateResult<'tcx, Ty<'tcx>> {
+        self.inner
+            .borrow_mut()
+            .float_unification_table()
+            .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))
+    }
+}
+
+impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
+    pub fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    pub fn equate<'a>(&'a mut self, a_is_expected: bool) -> Equate<'a, 'infcx, 'tcx> {
+        Equate::new(self, a_is_expected)
+    }
+
+    pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'tcx> {
+        Sub::new(self, a_is_expected)
+    }
+
+    pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'tcx> {
+        Lub::new(self, a_is_expected)
+    }
+
+    pub fn glb<'a>(&'a mut self, a_is_expected: bool) -> Glb<'a, 'infcx, 'tcx> {
+        Glb::new(self, a_is_expected)
+    }
+
+    /// Here, `dir` is either `EqTo`, `SubtypeOf`, or `SupertypeOf`.
+    /// The idea is that we should ensure that the type `a_ty` is equal
+    /// to, a subtype of, or a supertype of (respectively) the type
+    /// to which `b_vid` is bound.
+    ///
+    /// Since `b_vid` has not yet been instantiated with a type, we
+    /// will first instantiate `b_vid` with a *generalized* version
+    /// of `a_ty`. Generalization introduces other inference
+    /// variables wherever subtyping could occur.
+    pub fn instantiate(
+        &mut self,
+        a_ty: Ty<'tcx>,
+        dir: RelationDir,
+        b_vid: ty::TyVid,
+        a_is_expected: bool,
+    ) -> RelateResult<'tcx, ()> {
+        use self::RelationDir::*;
+
+        // Get the actual variable that b_vid has been inferred to
+        debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown());
+
+        debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", a_ty, dir, b_vid);
+
+        // Generalize type of `a_ty` appropriately depending on the
+        // direction.  As an example, assume:
+        //
+        // - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an
+        //   inference variable,
+        // - and `dir` == `SubtypeOf`.
+        //
+        // Then the generalized form `b_ty` would be `&'?2 ?3`, where
+        // `'?2` and `?3` are fresh region/type inference
+        // variables. (Down below, we will relate `a_ty <: b_ty`,
+        // adding constraints like `'x: '?2` and `?1 <: ?3`.)
+        let Generalization { ty: b_ty, needs_wf } = self.generalize(a_ty, b_vid, dir)?;
+        debug!(
+            "instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})",
+            a_ty, dir, b_vid, b_ty
+        );
+        self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
+
+        if needs_wf {
+            self.obligations.push(Obligation::new(
+                self.trace.cause.clone(),
+                self.param_env,
+                ty::PredicateKind::WellFormed(b_ty.into()).to_predicate(self.infcx.tcx),
+            ));
+        }
+
+        // Finally, relate `b_ty` to `a_ty`, as described in previous comment.
+        //
+        // FIXME(#16847): This code is non-ideal because all these subtype
+        // relations wind up attributed to the same spans. We need
+        // 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(
+                ty::Contravariant,
+                ty::VarianceDiagInfo::default(),
+                a_ty,
+                b_ty,
+            ),
+        }?;
+
+        Ok(())
+    }
+
+    /// Attempts to generalize `ty` for the type variable `for_vid`.
+    /// This checks for cycle -- that is, whether the type `ty`
+    /// references `for_vid`. The `dir` is the "direction" for which we
+    /// a performing the generalization (i.e., are we producing a type
+    /// that can be used as a supertype etc).
+    ///
+    /// Preconditions:
+    ///
+    /// - `for_vid` is a "root vid"
+    fn generalize(
+        &self,
+        ty: Ty<'tcx>,
+        for_vid: ty::TyVid,
+        dir: RelationDir,
+    ) -> RelateResult<'tcx, Generalization<'tcx>> {
+        debug!("generalize(ty={:?}, for_vid={:?}, dir={:?}", ty, for_vid, dir);
+        // Determine the ambient variance within which `ty` appears.
+        // The surrounding equation is:
+        //
+        //     ty [op] ty2
+        //
+        // where `op` is either `==`, `<:`, or `:>`. This maps quite
+        // naturally.
+        let ambient_variance = match dir {
+            RelationDir::EqTo => ty::Invariant,
+            RelationDir::SubtypeOf => ty::Covariant,
+            RelationDir::SupertypeOf => ty::Contravariant,
+        };
+
+        debug!("generalize: ambient_variance = {:?}", ambient_variance);
+
+        let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) {
+            v @ TypeVariableValue::Known { .. } => {
+                bug!("instantiating {:?} which has a known value {:?}", for_vid, v,)
+            }
+            TypeVariableValue::Unknown { universe } => universe,
+        };
+
+        debug!("generalize: for_universe = {:?}", for_universe);
+        debug!("generalize: trace = {:?}", self.trace);
+
+        let mut generalize = Generalizer {
+            infcx: self.infcx,
+            cause: &self.trace.cause,
+            for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid),
+            for_universe,
+            ambient_variance,
+            needs_wf: false,
+            root_ty: ty,
+            param_env: self.param_env,
+            cache: SsoHashMap::new(),
+        };
+
+        let ty = match generalize.relate(ty, ty) {
+            Ok(ty) => ty,
+            Err(e) => {
+                debug!("generalize: failure {:?}", e);
+                return Err(e);
+            }
+        };
+        let needs_wf = generalize.needs_wf;
+        debug!("generalize: success {{ {:?}, {:?} }}", ty, needs_wf);
+        Ok(Generalization { ty, needs_wf })
+    }
+
+    pub fn add_const_equate_obligation(
+        &mut self,
+        a_is_expected: bool,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) {
+        let predicate = if a_is_expected {
+            ty::PredicateKind::ConstEquate(a, b)
+        } else {
+            ty::PredicateKind::ConstEquate(b, a)
+        };
+        self.obligations.push(Obligation::new(
+            self.trace.cause.clone(),
+            self.param_env,
+            predicate.to_predicate(self.tcx()),
+        ));
+    }
+}
+
+struct Generalizer<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'cx, 'tcx>,
+
+    /// The span, used when creating new type variables and things.
+    cause: &'cx ObligationCause<'tcx>,
+
+    /// The vid of the type variable that is in the process of being
+    /// instantiated; if we find this within the type we are folding,
+    /// that means we would have created a cyclic type.
+    for_vid_sub_root: ty::TyVid,
+
+    /// The universe of the type variable that is in the process of
+    /// being instantiated. Any fresh variables that we create in this
+    /// process should be in that same universe.
+    for_universe: ty::UniverseIndex,
+
+    /// Track the variance as we descend into the type.
+    ambient_variance: ty::Variance,
+
+    /// See the field `needs_wf` in `Generalization`.
+    needs_wf: bool,
+
+    /// The root type that we are generalizing. Used when reporting cycles.
+    root_ty: Ty<'tcx>,
+
+    param_env: ty::ParamEnv<'tcx>,
+
+    cache: SsoHashMap<Ty<'tcx>, RelateResult<'tcx, Ty<'tcx>>>,
+}
+
+/// Result from a generalization operation. This includes
+/// not only the generalized type, but also a bool flag
+/// indicating whether further WF checks are needed.
+struct Generalization<'tcx> {
+    ty: Ty<'tcx>,
+
+    /// If true, then the generalized type may not be well-formed,
+    /// even if the source type is well-formed, so we should add an
+    /// additional check to enforce that it is. This arises in
+    /// particular around 'bivariant' type parameters that are only
+    /// constrained by a where-clause. As an example, imagine a type:
+    ///
+    ///     struct Foo<A, B> where A: Iterator<Item = B> {
+    ///         data: A
+    ///     }
+    ///
+    /// here, `A` will be covariant, but `B` is
+    /// unconstrained. However, whatever it is, for `Foo` to be WF, it
+    /// must be equal to `A::Item`. If we have an input `Foo<?A, ?B>`,
+    /// then after generalization we will wind up with a type like
+    /// `Foo<?C, ?D>`. When we enforce that `Foo<?A, ?B> <: Foo<?C,
+    /// ?D>` (or `>:`), we will wind up with the requirement that `?A
+    /// <: ?C`, but no particular relationship between `?B` and `?D`
+    /// (after all, we do not know the variance of the normalized form
+    /// of `A::Item` with respect to `A`). If we do nothing else, this
+    /// may mean that `?D` goes unconstrained (as in #41677). So, in
+    /// this scenario where we create a new type variable in a
+    /// bivariant context, we set the `needs_wf` flag to true. This
+    /// will force the calling code to check that `WF(Foo<?C, ?D>)`
+    /// holds, which in turn implies that `?C::Item == ?D`. So once
+    /// `?C` is constrained, that should suffice to restrict `?D`.
+    needs_wf: bool,
+}
+
+impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.param_env
+    }
+
+    fn tag(&self) -> &'static str {
+        "Generalizer"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
+    }
+
+    fn relate_item_substs(
+        &mut self,
+        item_def_id: DefId,
+        a_subst: SubstsRef<'tcx>,
+        b_subst: SubstsRef<'tcx>,
+    ) -> RelateResult<'tcx, SubstsRef<'tcx>> {
+        if self.ambient_variance == ty::Variance::Invariant {
+            // Avoid fetching the variance if we are in an invariant
+            // context; no need, and it can induce dependency cycles
+            // (e.g., #41849).
+            relate::relate_substs(self, None, a_subst, b_subst)
+        } else {
+            let opt_variances = self.tcx().variances_of(item_def_id);
+            relate::relate_substs(self, Some(&opt_variances), a_subst, b_subst)
+        }
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+
+        let result = self.relate(a, b);
+        self.ambient_variance = old_ambient_variance;
+        result
+    }
+
+    fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
+
+        if let Some(result) = self.cache.get(&t) {
+            return result.clone();
+        }
+        debug!("generalize: t={:?}", t);
+
+        // Check to see whether the type we are generalizing references
+        // any other type variable related to `vid` via
+        // subtyping. This is basically our "occurs check", preventing
+        // us from creating infinitely sized types.
+        let result = match *t.kind() {
+            ty::Infer(ty::TyVar(vid)) => {
+                let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
+                let sub_vid = self.infcx.inner.borrow_mut().type_variables().sub_root_var(vid);
+                if sub_vid == self.for_vid_sub_root {
+                    // If sub-roots are equal, then `for_vid` and
+                    // `vid` are related via subtyping.
+                    Err(TypeError::CyclicTy(self.root_ty))
+                } else {
+                    let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid);
+                    match probe {
+                        TypeVariableValue::Known { value: u } => {
+                            debug!("generalize: known value {:?}", u);
+                            self.relate(u, u)
+                        }
+                        TypeVariableValue::Unknown { universe } => {
+                            match self.ambient_variance {
+                                // Invariant: no need to make a fresh type variable.
+                                ty::Invariant => {
+                                    if self.for_universe.can_name(universe) {
+                                        return Ok(t);
+                                    }
+                                }
+
+                                // Bivariant: make a fresh var, but we
+                                // may need a WF predicate. See
+                                // comment on `needs_wf` field for
+                                // more info.
+                                ty::Bivariant => self.needs_wf = true,
+
+                                // Co/contravariant: this will be
+                                // sufficiently constrained later on.
+                                ty::Covariant | ty::Contravariant => (),
+                            }
+
+                            let origin =
+                                *self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
+                            let new_var_id = self
+                                .infcx
+                                .inner
+                                .borrow_mut()
+                                .type_variables()
+                                .new_var(self.for_universe, false, origin);
+                            let u = self.tcx().mk_ty_var(new_var_id);
+                            debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);
+                            Ok(u)
+                        }
+                    }
+                }
+            }
+            ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
+                // No matter what mode we are in,
+                // integer/floating-point types must be equal to be
+                // relatable.
+                Ok(t)
+            }
+            _ => relate::super_relate_tys(self, t, t),
+        };
+
+        self.cache.insert(t, result.clone());
+        return result;
+    }
+
+    fn regions(
+        &mut self,
+        r: ty::Region<'tcx>,
+        r2: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        assert_eq!(r, r2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
+
+        debug!("generalize: regions r={:?}", r);
+
+        match *r {
+            // Never make variables for regions bound within the type itself,
+            // nor for erased regions.
+            ty::ReLateBound(..) | ty::ReErased => {
+                return Ok(r);
+            }
+
+            ty::RePlaceholder(..)
+            | ty::ReVar(..)
+            | ty::ReEmpty(_)
+            | ty::ReStatic
+            | ty::ReEarlyBound(..)
+            | ty::ReFree(..) => {
+                // see common code below
+            }
+        }
+
+        // If we are in an invariant context, we can re-use the region
+        // as is, unless it happens to be in some universe that we
+        // can't name. (In the case of a region *variable*, we could
+        // use it if we promoted it into our universe, but we don't
+        // bother.)
+        if let ty::Invariant = self.ambient_variance {
+            let r_universe = self.infcx.universe_of_region(r);
+            if self.for_universe.can_name(r_universe) {
+                return Ok(r);
+            }
+        }
+
+        // FIXME: This is non-ideal because we don't give a
+        // very descriptive origin for this region variable.
+        Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.cause.span), self.for_universe))
+    }
+
+    fn consts(
+        &mut self,
+        c: &'tcx ty::Const<'tcx>,
+        c2: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        assert_eq!(c, c2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
+
+        match c.val {
+            ty::ConstKind::Infer(InferConst::Var(vid)) => {
+                let mut inner = self.infcx.inner.borrow_mut();
+                let variable_table = &mut inner.const_unification_table();
+                let var_value = variable_table.probe_value(vid);
+                match var_value.val {
+                    ConstVariableValue::Known { value: u } => {
+                        drop(inner);
+                        self.relate(u, u)
+                    }
+                    ConstVariableValue::Unknown { universe } => {
+                        if self.for_universe.can_name(universe) {
+                            Ok(c)
+                        } else {
+                            let new_var_id = variable_table.new_key(ConstVarValue {
+                                origin: var_value.origin,
+                                val: ConstVariableValue::Unknown { universe: self.for_universe },
+                            });
+                            Ok(self.tcx().mk_const_var(new_var_id, c.ty))
+                        }
+                    }
+                }
+            }
+            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted })
+                if self.tcx().lazy_normalization() =>
+            {
+                assert_eq!(promoted, None);
+                let substs = self.relate_with_variance(
+                    ty::Variance::Invariant,
+                    ty::VarianceDiagInfo::default(),
+                    substs,
+                    substs,
+                )?;
+                Ok(self.tcx().mk_const(ty::Const {
+                    ty: c.ty,
+                    val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
+                }))
+            }
+            _ => relate::super_relate_consts(self, c, c),
+        }
+    }
+}
+
+pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> {
+    /// Register an obligation that both constants must be equal to each other.
+    ///
+    /// If they aren't equal then the relation doesn't hold.
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
+}
+
+pub trait RelateResultCompare<'tcx, T> {
+    fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T>
+    where
+        F: FnOnce() -> TypeError<'tcx>;
+}
+
+impl<'tcx, T: Clone + PartialEq> RelateResultCompare<'tcx, T> for RelateResult<'tcx, T> {
+    fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T>
+    where
+        F: FnOnce() -> TypeError<'tcx>,
+    {
+        self.clone().and_then(|s| if s == t { self.clone() } else { Err(f()) })
+    }
+}
+
+pub fn const_unification_error<'tcx>(
+    a_is_expected: bool,
+    (a, b): (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>),
+) -> TypeError<'tcx> {
+    TypeError::ConstMismatch(ty::relate::expected_found_bool(a_is_expected, a, b))
+}
+
+fn int_unification_error<'tcx>(
+    a_is_expected: bool,
+    v: (ty::IntVarValue, ty::IntVarValue),
+) -> TypeError<'tcx> {
+    let (a, b) = v;
+    TypeError::IntMismatch(ty::relate::expected_found_bool(a_is_expected, a, b))
+}
+
+fn float_unification_error<'tcx>(
+    a_is_expected: bool,
+    v: (ty::FloatVarValue, ty::FloatVarValue),
+) -> TypeError<'tcx> {
+    let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
+    TypeError::FloatMismatch(ty::relate::expected_found_bool(a_is_expected, a, b))
+}
+
+struct ConstInferUnifier<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'cx, 'tcx>,
+
+    span: Span,
+
+    param_env: ty::ParamEnv<'tcx>,
+
+    for_universe: ty::UniverseIndex,
+
+    /// The vid of the const variable that is in the process of being
+    /// instantiated; if we find this within the const we are folding,
+    /// that means we would have created a cyclic const.
+    target_vid: ty::ConstVid<'tcx>,
+}
+
+// We use `TypeRelation` here to propagate `RelateResult` upwards.
+//
+// Both inputs are expected to be the same.
+impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.param_env
+    }
+
+    fn tag(&self) -> &'static str {
+        "ConstInferUnifier"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        _variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        // We don't care about variance here.
+        self.relate(a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
+    }
+
+    fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        debug_assert_eq!(t, _t);
+        debug!("ConstInferUnifier: t={:?}", t);
+
+        match t.kind() {
+            &ty::Infer(ty::TyVar(vid)) => {
+                let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
+                let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid);
+                match probe {
+                    TypeVariableValue::Known { value: u } => {
+                        debug!("ConstOccursChecker: known value {:?}", u);
+                        self.tys(u, u)
+                    }
+                    TypeVariableValue::Unknown { universe } => {
+                        if self.for_universe.can_name(universe) {
+                            return Ok(t);
+                        }
+
+                        let origin =
+                            *self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
+                        let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var(
+                            self.for_universe,
+                            false,
+                            origin,
+                        );
+                        let u = self.tcx().mk_ty_var(new_var_id);
+                        debug!(
+                            "ConstInferUnifier: replacing original vid={:?} with new={:?}",
+                            vid, u
+                        );
+                        Ok(u)
+                    }
+                }
+            }
+            ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => Ok(t),
+            _ => relate::super_relate_tys(self, t, t),
+        }
+    }
+
+    fn regions(
+        &mut self,
+        r: ty::Region<'tcx>,
+        _r: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug_assert_eq!(r, _r);
+        debug!("ConstInferUnifier: r={:?}", r);
+
+        match r {
+            // Never make variables for regions bound within the type itself,
+            // nor for erased regions.
+            ty::ReLateBound(..) | ty::ReErased => {
+                return Ok(r);
+            }
+
+            ty::RePlaceholder(..)
+            | ty::ReVar(..)
+            | ty::ReEmpty(_)
+            | ty::ReStatic
+            | ty::ReEarlyBound(..)
+            | ty::ReFree(..) => {
+                // see common code below
+            }
+        }
+
+        let r_universe = self.infcx.universe_of_region(r);
+        if self.for_universe.can_name(r_universe) {
+            return Ok(r);
+        } else {
+            // FIXME: This is non-ideal because we don't give a
+            // very descriptive origin for this region variable.
+            Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe))
+        }
+    }
+
+    fn consts(
+        &mut self,
+        c: &'tcx ty::Const<'tcx>,
+        _c: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        debug_assert_eq!(c, _c);
+        debug!("ConstInferUnifier: c={:?}", c);
+
+        match c.val {
+            ty::ConstKind::Infer(InferConst::Var(vid)) => {
+                let mut inner = self.infcx.inner.borrow_mut();
+                let variable_table = &mut inner.const_unification_table();
+
+                // Check if the current unification would end up
+                // unifying `target_vid` with a const which contains
+                // an inference variable which is unioned with `target_vid`.
+                //
+                // Not doing so can easily result in stack overflows.
+                if variable_table.unioned(self.target_vid, vid) {
+                    return Err(TypeError::CyclicConst(c));
+                }
+
+                let var_value = variable_table.probe_value(vid);
+                match var_value.val {
+                    ConstVariableValue::Known { value: u } => self.consts(u, u),
+                    ConstVariableValue::Unknown { universe } => {
+                        if self.for_universe.can_name(universe) {
+                            Ok(c)
+                        } else {
+                            let new_var_id = variable_table.new_key(ConstVarValue {
+                                origin: var_value.origin,
+                                val: ConstVariableValue::Unknown { universe: self.for_universe },
+                            });
+                            Ok(self.tcx().mk_const_var(new_var_id, c.ty))
+                        }
+                    }
+                }
+            }
+            ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted })
+                if self.tcx().lazy_normalization() =>
+            {
+                assert_eq!(promoted, None);
+                let substs = self.relate_with_variance(
+                    ty::Variance::Invariant,
+                    ty::VarianceDiagInfo::default(),
+                    substs,
+                    substs,
+                )?;
+                Ok(self.tcx().mk_const(ty::Const {
+                    ty: c.ty,
+                    val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
+                }))
+            }
+            _ => relate::super_relate_consts(self, c, c),
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs
new file mode 100644
index 00000000000..0c93271a1ae
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/equate.rs
@@ -0,0 +1,149 @@
+use super::combine::{CombineFields, ConstEquateRelation, RelationDir};
+use super::Subtype;
+
+use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::TyVar;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+use rustc_hir::def_id::DefId;
+
+/// Ensures `a` is made equal to `b`. Returns `a` on success.
+pub struct Equate<'combine, 'infcx, 'tcx> {
+    fields: &'combine mut CombineFields<'infcx, 'tcx>,
+    a_is_expected: bool,
+}
+
+impl<'combine, 'infcx, 'tcx> Equate<'combine, 'infcx, 'tcx> {
+    pub fn new(
+        fields: &'combine mut CombineFields<'infcx, 'tcx>,
+        a_is_expected: bool,
+    ) -> Equate<'combine, 'infcx, 'tcx> {
+        Equate { fields, a_is_expected }
+    }
+}
+
+impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
+    fn tag(&self) -> &'static str {
+        "Equate"
+    }
+
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.fields.tcx()
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.fields.param_env
+    }
+
+    fn a_is_expected(&self) -> bool {
+        self.a_is_expected
+    }
+
+    fn relate_item_substs(
+        &mut self,
+        _item_def_id: DefId,
+        a_subst: SubstsRef<'tcx>,
+        b_subst: SubstsRef<'tcx>,
+    ) -> RelateResult<'tcx, SubstsRef<'tcx>> {
+        // N.B., once we are equating types, we don't care about
+        // variance, so don't try to lookup the variance here. This
+        // also avoids some cycles (e.g., #41849) since looking up
+        // variance requires computing types which can require
+        // performing trait matching (which then performs equality
+        // unification).
+
+        relate::relate_substs(self, None, a_subst, b_subst)
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        _: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        self.relate(a, b)
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        debug!("{}.tys({:?}, {:?})", self.tag(), a, b);
+        if a == b {
+            return Ok(a);
+        }
+
+        let infcx = self.fields.infcx;
+        let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
+        let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
+
+        debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b);
+
+        match (a.kind(), b.kind()) {
+            (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
+                infcx.inner.borrow_mut().type_variables().equate(a_id, b_id);
+            }
+
+            (&ty::Infer(TyVar(a_id)), _) => {
+                self.fields.instantiate(b, RelationDir::EqTo, a_id, self.a_is_expected)?;
+            }
+
+            (_, &ty::Infer(TyVar(b_id))) => {
+                self.fields.instantiate(a, RelationDir::EqTo, b_id, self.a_is_expected)?;
+            }
+
+            _ => {
+                self.fields.infcx.super_combine_tys(self, a, b)?;
+            }
+        }
+
+        Ok(a)
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
+        let origin = Subtype(box self.fields.trace.clone());
+        self.fields
+            .infcx
+            .inner
+            .borrow_mut()
+            .unwrap_region_constraints()
+            .make_eqregion(origin, a, b);
+        Ok(a)
+    }
+
+    fn consts(
+        &mut self,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        self.fields.infcx.super_combine_consts(self, a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
+            self.fields.higher_ranked_sub(a, b, self.a_is_expected)?;
+            self.fields.higher_ranked_sub(b, a, self.a_is_expected)
+        } else {
+            // Fast path for the common case.
+            self.relate(a.skip_binder(), b.skip_binder())?;
+            Ok(a)
+        }
+    }
+}
+
+impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> {
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
new file mode 100644
index 00000000000..d5e1c061bf0
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -0,0 +1,2578 @@
+//! Error Reporting Code for the inference engine
+//!
+//! Because of the way inference, and in particular region inference,
+//! works, it often happens that errors are not detected until far after
+//! the relevant line of code has been type-checked. Therefore, there is
+//! an elaborate system to track why a particular constraint in the
+//! inference graph arose so that we can explain to the user what gave
+//! rise to a particular error.
+//!
+//! The basis of the system are the "origin" types. An "origin" is the
+//! reason that a constraint or inference variable arose. There are
+//! different "origin" enums for different kinds of constraints/variables
+//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
+//! a span, but also more information so that we can generate a meaningful
+//! error message.
+//!
+//! Having a catalog of all the different reasons an error can arise is
+//! also useful for other reasons, like cross-referencing FAQs etc, though
+//! we are not really taking advantage of this yet.
+//!
+//! # Region Inference
+//!
+//! Region inference is particularly tricky because it always succeeds "in
+//! the moment" and simply registers a constraint. Then, at the end, we
+//! can compute the full graph and report errors, so we need to be able to
+//! store and later report what gave rise to the conflicting constraints.
+//!
+//! # Subtype Trace
+//!
+//! Determining whether `T1 <: T2` often involves a number of subtypes and
+//! subconstraints along the way. A "TypeTrace" is an extended version
+//! of an origin that traces the types and other values that were being
+//! compared. It is not necessarily comprehensive (in fact, at the time of
+//! this writing it only tracks the root values being compared) but I'd
+//! like to extend it to include significant "waypoints". For example, if
+//! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2
+//! <: T4` fails, I'd like the trace to include enough information to say
+//! "in the 2nd element of the tuple". Similarly, failures when comparing
+//! arguments or return types in fn types should be able to cite the
+//! specific position, etc.
+//!
+//! # Reality vs plan
+//!
+//! Of course, there is still a LOT of code in typeck that has yet to be
+//! ported to this system, and which relies on string concatenation at the
+//! time of error detection.
+
+use super::lexical_region_resolve::RegionResolutionError;
+use super::region_constraints::GenericKind;
+use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
+
+use crate::infer;
+use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
+use crate::traits::error_reporting::report_object_safety_error;
+use crate::traits::{
+    IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+    StatementAsExpression,
+};
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err};
+use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{Item, ItemKind, Node};
+use rustc_middle::dep_graph::DepContext;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::{
+    self,
+    subst::{GenericArgKind, Subst, SubstsRef},
+    Region, Ty, TyCtxt, TypeFoldable,
+};
+use rustc_span::{sym, BytePos, DesugaringKind, MultiSpan, Pos, Span};
+use rustc_target::spec::abi;
+use std::ops::ControlFlow;
+use std::{cmp, fmt, iter};
+
+mod note;
+
+mod need_type_info;
+pub use need_type_info::TypeAnnotationNeeded;
+
+pub mod nice_region_error;
+
+pub(super) fn note_and_explain_region(
+    tcx: TyCtxt<'tcx>,
+    err: &mut DiagnosticBuilder<'_>,
+    prefix: &str,
+    region: ty::Region<'tcx>,
+    suffix: &str,
+    alt_span: Option<Span>,
+) {
+    let (description, span) = match *region {
+        ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
+            msg_span_from_free_region(tcx, region, alt_span)
+        }
+
+        ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), alt_span),
+
+        // uh oh, hope no user ever sees THIS
+        ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), alt_span),
+
+        ty::RePlaceholder(_) => return,
+
+        // FIXME(#13998) RePlaceholder should probably print like
+        // ReFree rather than dumping Debug output on the user.
+        //
+        // We shouldn't really be having unification failures with ReVar
+        // and ReLateBound though.
+        ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
+            (format!("lifetime {:?}", region), alt_span)
+        }
+    };
+
+    emit_msg_span(err, prefix, description, span, suffix);
+}
+
+pub(super) fn note_and_explain_free_region(
+    tcx: TyCtxt<'tcx>,
+    err: &mut DiagnosticBuilder<'_>,
+    prefix: &str,
+    region: ty::Region<'tcx>,
+    suffix: &str,
+) {
+    let (description, span) = msg_span_from_free_region(tcx, region, None);
+
+    emit_msg_span(err, prefix, description, span, suffix);
+}
+
+fn msg_span_from_free_region(
+    tcx: TyCtxt<'tcx>,
+    region: ty::Region<'tcx>,
+    alt_span: Option<Span>,
+) -> (String, Option<Span>) {
+    match *region {
+        ty::ReEarlyBound(_) | ty::ReFree(_) => {
+            msg_span_from_early_bound_and_free_regions(tcx, region)
+        }
+        ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
+        ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), alt_span),
+        ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), alt_span),
+        _ => bug!("{:?}", region),
+    }
+}
+
+fn msg_span_from_early_bound_and_free_regions(
+    tcx: TyCtxt<'tcx>,
+    region: ty::Region<'tcx>,
+) -> (String, Option<Span>) {
+    let sm = tcx.sess.source_map();
+
+    let scope = region.free_region_binding_scope(tcx);
+    let node = tcx.hir().local_def_id_to_hir_id(scope.expect_local());
+    let tag = match tcx.hir().find(node) {
+        Some(Node::Block(_) | Node::Expr(_)) => "body",
+        Some(Node::Item(it)) => item_scope_tag(&it),
+        Some(Node::TraitItem(it)) => trait_item_scope_tag(&it),
+        Some(Node::ImplItem(it)) => impl_item_scope_tag(&it),
+        Some(Node::ForeignItem(it)) => foreign_item_scope_tag(&it),
+        _ => unreachable!(),
+    };
+    let (prefix, span) = match *region {
+        ty::ReEarlyBound(ref br) => {
+            let mut sp = sm.guess_head_span(tcx.hir().span(node));
+            if let Some(param) =
+                tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
+            {
+                sp = param.span;
+            }
+            (format!("the lifetime `{}` as defined on", br.name), sp)
+        }
+        ty::ReFree(ty::FreeRegion {
+            bound_region: ty::BoundRegionKind::BrNamed(_, name), ..
+        }) => {
+            let mut sp = sm.guess_head_span(tcx.hir().span(node));
+            if let Some(param) =
+                tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
+            {
+                sp = param.span;
+            }
+            (format!("the lifetime `{}` as defined on", name), sp)
+        }
+        ty::ReFree(ref fr) => match fr.bound_region {
+            ty::BrAnon(idx) => {
+                if let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) {
+                    ("the anonymous lifetime defined on".to_string(), ty.span)
+                } else {
+                    (
+                        format!("the anonymous lifetime #{} defined on", idx + 1),
+                        tcx.hir().span(node),
+                    )
+                }
+            }
+            _ => (
+                format!("the lifetime `{}` as defined on", region),
+                sm.guess_head_span(tcx.hir().span(node)),
+            ),
+        },
+        _ => bug!(),
+    };
+    let (msg, opt_span) = explain_span(tcx, tag, span);
+    (format!("{} {}", prefix, msg), opt_span)
+}
+
+fn emit_msg_span(
+    err: &mut DiagnosticBuilder<'_>,
+    prefix: &str,
+    description: String,
+    span: Option<Span>,
+    suffix: &str,
+) {
+    let message = format!("{}{}{}", prefix, description, suffix);
+
+    if let Some(span) = span {
+        err.span_note(span, &message);
+    } else {
+        err.note(&message);
+    }
+}
+
+fn item_scope_tag(item: &hir::Item<'_>) -> &'static str {
+    match item.kind {
+        hir::ItemKind::Impl { .. } => "impl",
+        hir::ItemKind::Struct(..) => "struct",
+        hir::ItemKind::Union(..) => "union",
+        hir::ItemKind::Enum(..) => "enum",
+        hir::ItemKind::Trait(..) => "trait",
+        hir::ItemKind::Fn(..) => "function body",
+        _ => "item",
+    }
+}
+
+fn trait_item_scope_tag(item: &hir::TraitItem<'_>) -> &'static str {
+    match item.kind {
+        hir::TraitItemKind::Fn(..) => "method body",
+        hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => "associated item",
+    }
+}
+
+fn impl_item_scope_tag(item: &hir::ImplItem<'_>) -> &'static str {
+    match item.kind {
+        hir::ImplItemKind::Fn(..) => "method body",
+        hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(..) => "associated item",
+    }
+}
+
+fn foreign_item_scope_tag(item: &hir::ForeignItem<'_>) -> &'static str {
+    match item.kind {
+        hir::ForeignItemKind::Fn(..) => "method body",
+        hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => "associated item",
+    }
+}
+
+fn explain_span(tcx: TyCtxt<'tcx>, heading: &str, span: Span) -> (String, Option<Span>) {
+    let lo = tcx.sess.source_map().lookup_char_pos(span.lo());
+    (format!("the {} at {}:{}", heading, lo.line, lo.col.to_usize() + 1), Some(span))
+}
+
+pub fn unexpected_hidden_region_diagnostic(
+    tcx: TyCtxt<'tcx>,
+    span: Span,
+    hidden_ty: Ty<'tcx>,
+    hidden_region: ty::Region<'tcx>,
+) -> DiagnosticBuilder<'tcx> {
+    let mut err = struct_span_err!(
+        tcx.sess,
+        span,
+        E0700,
+        "hidden type for `impl Trait` captures lifetime that does not appear in bounds",
+    );
+
+    // Explain the region we are capturing.
+    match hidden_region {
+        ty::ReEmpty(ty::UniverseIndex::ROOT) => {
+            // All lifetimes shorter than the function body are `empty` in
+            // lexical region resolution. The default explanation of "an empty
+            // lifetime" isn't really accurate here.
+            let message = format!(
+                "hidden type `{}` captures lifetime smaller than the function body",
+                hidden_ty
+            );
+            err.span_note(span, &message);
+        }
+        ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) => {
+            // Assuming regionck succeeded (*), we ought to always be
+            // capturing *some* region from the fn header, and hence it
+            // ought to be free. So under normal circumstances, we will go
+            // down this path which gives a decent human readable
+            // explanation.
+            //
+            // (*) if not, the `tainted_by_errors` field would be set to
+            // `Some(ErrorReported)` in any case, so we wouldn't be here at all.
+            note_and_explain_free_region(
+                tcx,
+                &mut err,
+                &format!("hidden type `{}` captures ", hidden_ty),
+                hidden_region,
+                "",
+            );
+        }
+        _ => {
+            // Ugh. This is a painful case: the hidden region is not one
+            // that we can easily summarize or explain. This can happen
+            // in a case like
+            // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`:
+            //
+            // ```
+            // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> {
+            //   if condition() { a } else { b }
+            // }
+            // ```
+            //
+            // Here the captured lifetime is the intersection of `'a` and
+            // `'b`, which we can't quite express.
+
+            // We can at least report a really cryptic error for now.
+            note_and_explain_region(
+                tcx,
+                &mut err,
+                &format!("hidden type `{}` captures ", hidden_ty),
+                hidden_region,
+                "",
+                None,
+            );
+        }
+    }
+
+    err
+}
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    pub fn report_region_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>) {
+        debug!("report_region_errors(): {} errors to start", errors.len());
+
+        // try to pre-process the errors, which will group some of them
+        // together into a `ProcessedErrors` group:
+        let errors = self.process_errors(errors);
+
+        debug!("report_region_errors: {} errors after preprocessing", errors.len());
+
+        for error in errors {
+            debug!("report_region_errors: error = {:?}", error);
+
+            if !self.try_report_nice_region_error(&error) {
+                match error.clone() {
+                    // These errors could indicate all manner of different
+                    // problems with many different solutions. Rather
+                    // than generate a "one size fits all" error, what we
+                    // attempt to do is go through a number of specific
+                    // scenarios and try to find the best way to present
+                    // the error. If all of these fails, we fall back to a rather
+                    // general bit of code that displays the error information
+                    RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
+                        if sub.is_placeholder() || sup.is_placeholder() {
+                            self.report_placeholder_failure(origin, sub, sup).emit();
+                        } else {
+                            self.report_concrete_failure(origin, sub, sup).emit();
+                        }
+                    }
+
+                    RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => {
+                        self.report_generic_bound_failure(
+                            origin.span(),
+                            Some(origin),
+                            param_ty,
+                            sub,
+                        );
+                    }
+
+                    RegionResolutionError::SubSupConflict(
+                        _,
+                        var_origin,
+                        sub_origin,
+                        sub_r,
+                        sup_origin,
+                        sup_r,
+                    ) => {
+                        if sub_r.is_placeholder() {
+                            self.report_placeholder_failure(sub_origin, sub_r, sup_r).emit();
+                        } else if sup_r.is_placeholder() {
+                            self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit();
+                        } else {
+                            self.report_sub_sup_conflict(
+                                var_origin, sub_origin, sub_r, sup_origin, sup_r,
+                            );
+                        }
+                    }
+
+                    RegionResolutionError::UpperBoundUniverseConflict(
+                        _,
+                        _,
+                        var_universe,
+                        sup_origin,
+                        sup_r,
+                    ) => {
+                        assert!(sup_r.is_placeholder());
+
+                        // Make a dummy value for the "sub region" --
+                        // this is the initial value of the
+                        // placeholder. In practice, we expect more
+                        // tailored errors that don't really use this
+                        // value.
+                        let sub_r = self.tcx.mk_region(ty::ReEmpty(var_universe));
+
+                        self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit();
+                    }
+
+                    RegionResolutionError::MemberConstraintFailure {
+                        hidden_ty,
+                        member_region,
+                        span,
+                    } => {
+                        let hidden_ty = self.resolve_vars_if_possible(hidden_ty);
+                        unexpected_hidden_region_diagnostic(
+                            self.tcx,
+                            span,
+                            hidden_ty,
+                            member_region,
+                        )
+                        .emit();
+                    }
+                }
+            }
+        }
+    }
+
+    // This method goes through all the errors and try to group certain types
+    // of error together, for the purpose of suggesting explicit lifetime
+    // parameters to the user. This is done so that we can have a more
+    // complete view of what lifetimes should be the same.
+    // If the return value is an empty vector, it means that processing
+    // failed (so the return value of this method should not be used).
+    //
+    // The method also attempts to weed out messages that seem like
+    // duplicates that will be unhelpful to the end-user. But
+    // obviously it never weeds out ALL errors.
+    fn process_errors(
+        &self,
+        errors: &[RegionResolutionError<'tcx>],
+    ) -> Vec<RegionResolutionError<'tcx>> {
+        debug!("process_errors()");
+
+        // We want to avoid reporting generic-bound failures if we can
+        // avoid it: these have a very high rate of being unhelpful in
+        // practice. This is because they are basically secondary
+        // checks that test the state of the region graph after the
+        // rest of inference is done, and the other kinds of errors
+        // indicate that the region constraint graph is internally
+        // inconsistent, so these test results are likely to be
+        // meaningless.
+        //
+        // Therefore, we filter them out of the list unless they are
+        // the only thing in the list.
+
+        let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
+            RegionResolutionError::GenericBoundFailure(..) => true,
+            RegionResolutionError::ConcreteFailure(..)
+            | RegionResolutionError::SubSupConflict(..)
+            | RegionResolutionError::UpperBoundUniverseConflict(..)
+            | RegionResolutionError::MemberConstraintFailure { .. } => false,
+        };
+
+        let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
+            errors.to_owned()
+        } else {
+            errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
+        };
+
+        // sort the errors by span, for better error message stability.
+        errors.sort_by_key(|u| match *u {
+            RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
+            RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
+            RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(),
+            RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
+            RegionResolutionError::MemberConstraintFailure { span, .. } => span,
+        });
+        errors
+    }
+
+    /// Adds a note if the types come from similarly named crates
+    fn check_and_note_conflicting_crates(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        terr: &TypeError<'tcx>,
+    ) {
+        use hir::def_id::CrateNum;
+        use rustc_hir::definitions::DisambiguatedDefPathData;
+        use ty::print::Printer;
+        use ty::subst::GenericArg;
+
+        struct AbsolutePathPrinter<'tcx> {
+            tcx: TyCtxt<'tcx>,
+        }
+
+        struct NonTrivialPath;
+
+        impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
+            type Error = NonTrivialPath;
+
+            type Path = Vec<String>;
+            type Region = !;
+            type Type = !;
+            type DynExistential = !;
+            type Const = !;
+
+            fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+                self.tcx
+            }
+
+            fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
+                Err(NonTrivialPath)
+            }
+
+            fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
+                Err(NonTrivialPath)
+            }
+
+            fn print_dyn_existential(
+                self,
+                _predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
+            ) -> Result<Self::DynExistential, Self::Error> {
+                Err(NonTrivialPath)
+            }
+
+            fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
+                Err(NonTrivialPath)
+            }
+
+            fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
+                Ok(vec![self.tcx.crate_name(cnum).to_string()])
+            }
+            fn path_qualified(
+                self,
+                _self_ty: Ty<'tcx>,
+                _trait_ref: Option<ty::TraitRef<'tcx>>,
+            ) -> Result<Self::Path, Self::Error> {
+                Err(NonTrivialPath)
+            }
+
+            fn path_append_impl(
+                self,
+                _print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+                _disambiguated_data: &DisambiguatedDefPathData,
+                _self_ty: Ty<'tcx>,
+                _trait_ref: Option<ty::TraitRef<'tcx>>,
+            ) -> Result<Self::Path, Self::Error> {
+                Err(NonTrivialPath)
+            }
+            fn path_append(
+                self,
+                print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+                disambiguated_data: &DisambiguatedDefPathData,
+            ) -> Result<Self::Path, Self::Error> {
+                let mut path = print_prefix(self)?;
+                path.push(disambiguated_data.to_string());
+                Ok(path)
+            }
+            fn path_generic_args(
+                self,
+                print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+                _args: &[GenericArg<'tcx>],
+            ) -> Result<Self::Path, Self::Error> {
+                print_prefix(self)
+            }
+        }
+
+        let report_path_match = |err: &mut DiagnosticBuilder<'_>, did1: DefId, did2: DefId| {
+            // Only external crates, if either is from a local
+            // module we could have false positives
+            if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
+                let abs_path =
+                    |def_id| AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]);
+
+                // We compare strings because DefPath can be different
+                // for imported and non-imported crates
+                let same_path = || -> Result<_, NonTrivialPath> {
+                    Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2)
+                        || abs_path(did1)? == abs_path(did2)?)
+                };
+                if same_path().unwrap_or(false) {
+                    let crate_name = self.tcx.crate_name(did1.krate);
+                    err.note(&format!(
+                        "perhaps two different versions of crate `{}` are being used?",
+                        crate_name
+                    ));
+                }
+            }
+        };
+        match *terr {
+            TypeError::Sorts(ref exp_found) => {
+                // if they are both "path types", there's a chance of ambiguity
+                // due to different versions of the same crate
+                if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) =
+                    (exp_found.expected.kind(), exp_found.found.kind())
+                {
+                    report_path_match(err, exp_adt.did, found_adt.did);
+                }
+            }
+            TypeError::Traits(ref exp_found) => {
+                report_path_match(err, exp_found.expected, exp_found.found);
+            }
+            _ => (), // FIXME(#22750) handle traits and stuff
+        }
+    }
+
+    fn note_error_origin(
+        &self,
+        err: &mut DiagnosticBuilder<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
+    ) {
+        match cause.code {
+            ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
+                let ty = self.resolve_vars_if_possible(root_ty);
+                if ty.is_suggestable() {
+                    // don't show type `_`
+                    err.span_label(span, format!("this expression has type `{}`", ty));
+                }
+                if let Some(ty::error::ExpectedFound { found, .. }) = exp_found {
+                    if ty.is_box() && ty.boxed_ty() == found {
+                        if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                            err.span_suggestion(
+                                span,
+                                "consider dereferencing the boxed value",
+                                format!("*{}", snippet),
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                    }
+                }
+            }
+            ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => {
+                err.span_label(span, "expected due to this");
+            }
+            ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+                semi_span,
+                source,
+                ref prior_arms,
+                last_ty,
+                scrut_hir_id,
+                opt_suggest_box_span,
+                arm_span,
+                scrut_span,
+                ..
+            }) => match source {
+                hir::MatchSource::IfLetDesugar { .. } => {
+                    let msg = "`if let` arms have incompatible types";
+                    err.span_label(cause.span, msg);
+                    if let Some(ret_sp) = opt_suggest_box_span {
+                        self.suggest_boxing_for_return_impl_trait(
+                            err,
+                            ret_sp,
+                            prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
+                        );
+                    }
+                }
+                hir::MatchSource::TryDesugar => {
+                    if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
+                        let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
+                        let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
+                            let arg_expr = args.first().expect("try desugaring call w/out arg");
+                            self.in_progress_typeck_results.and_then(|typeck_results| {
+                                typeck_results.borrow().expr_ty_opt(arg_expr)
+                            })
+                        } else {
+                            bug!("try desugaring w/out call expr as scrutinee");
+                        };
+
+                        match scrut_ty {
+                            Some(ty) if expected == ty => {
+                                let source_map = self.tcx.sess.source_map();
+                                err.span_suggestion(
+                                    source_map.end_point(cause.span),
+                                    "try removing this `?`",
+                                    "".to_string(),
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+                _ => {
+                    // `last_ty` can be `!`, `expected` will have better info when present.
+                    let t = self.resolve_vars_if_possible(match exp_found {
+                        Some(ty::error::ExpectedFound { expected, .. }) => expected,
+                        _ => last_ty,
+                    });
+                    let source_map = self.tcx.sess.source_map();
+                    let mut any_multiline_arm = source_map.is_multiline(arm_span);
+                    if prior_arms.len() <= 4 {
+                        for sp in prior_arms {
+                            any_multiline_arm |= source_map.is_multiline(*sp);
+                            err.span_label(*sp, format!("this is found to be of type `{}`", t));
+                        }
+                    } else if let Some(sp) = prior_arms.last() {
+                        any_multiline_arm |= source_map.is_multiline(*sp);
+                        err.span_label(
+                            *sp,
+                            format!("this and all prior arms are found to be of type `{}`", t),
+                        );
+                    }
+                    let outer_error_span = if any_multiline_arm {
+                        // Cover just `match` and the scrutinee expression, not
+                        // the entire match body, to reduce diagram noise.
+                        cause.span.shrink_to_lo().to(scrut_span)
+                    } else {
+                        cause.span
+                    };
+                    let msg = "`match` arms have incompatible types";
+                    err.span_label(outer_error_span, msg);
+                    if let Some((sp, boxed)) = semi_span {
+                        if let (StatementAsExpression::NeedsBoxing, [.., prior_arm]) =
+                            (boxed, &prior_arms[..])
+                        {
+                            err.multipart_suggestion(
+                                "consider removing this semicolon and boxing the expressions",
+                                vec![
+                                    (prior_arm.shrink_to_lo(), "Box::new(".to_string()),
+                                    (prior_arm.shrink_to_hi(), ")".to_string()),
+                                    (arm_span.shrink_to_lo(), "Box::new(".to_string()),
+                                    (arm_span.shrink_to_hi(), ")".to_string()),
+                                    (sp, String::new()),
+                                ],
+                                Applicability::HasPlaceholders,
+                            );
+                        } else if matches!(boxed, StatementAsExpression::NeedsBoxing) {
+                            err.span_suggestion_short(
+                                sp,
+                                "consider removing this semicolon and boxing the expressions",
+                                String::new(),
+                                Applicability::MachineApplicable,
+                            );
+                        } else {
+                            err.span_suggestion_short(
+                                sp,
+                                "consider removing this semicolon",
+                                String::new(),
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                    }
+                    if let Some(ret_sp) = opt_suggest_box_span {
+                        // Get return type span and point to it.
+                        self.suggest_boxing_for_return_impl_trait(
+                            err,
+                            ret_sp,
+                            prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
+                        );
+                    }
+                }
+            },
+            ObligationCauseCode::IfExpression(box IfExpressionCause {
+                then,
+                else_sp,
+                outer,
+                semicolon,
+                opt_suggest_box_span,
+            }) => {
+                err.span_label(then, "expected because of this");
+                if let Some(sp) = outer {
+                    err.span_label(sp, "`if` and `else` have incompatible types");
+                }
+                if let Some((sp, boxed)) = semicolon {
+                    if matches!(boxed, StatementAsExpression::NeedsBoxing) {
+                        err.multipart_suggestion(
+                            "consider removing this semicolon and boxing the expression",
+                            vec![
+                                (then.shrink_to_lo(), "Box::new(".to_string()),
+                                (then.shrink_to_hi(), ")".to_string()),
+                                (else_sp.shrink_to_lo(), "Box::new(".to_string()),
+                                (else_sp.shrink_to_hi(), ")".to_string()),
+                                (sp, String::new()),
+                            ],
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        err.span_suggestion_short(
+                            sp,
+                            "consider removing this semicolon",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+                if let Some(ret_sp) = opt_suggest_box_span {
+                    self.suggest_boxing_for_return_impl_trait(
+                        err,
+                        ret_sp,
+                        vec![then, else_sp].into_iter(),
+                    );
+                }
+            }
+            _ => (),
+        }
+    }
+
+    fn suggest_boxing_for_return_impl_trait(
+        &self,
+        err: &mut DiagnosticBuilder<'tcx>,
+        return_sp: Span,
+        arm_spans: impl Iterator<Item = Span>,
+    ) {
+        err.multipart_suggestion(
+            "you could change the return type to be a boxed trait object",
+            vec![
+                (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
+                (return_sp.shrink_to_hi(), ">".to_string()),
+            ],
+            Applicability::MaybeIncorrect,
+        );
+        let sugg = arm_spans
+            .flat_map(|sp| {
+                vec![
+                    (sp.shrink_to_lo(), "Box::new(".to_string()),
+                    (sp.shrink_to_hi(), ")".to_string()),
+                ]
+                .into_iter()
+            })
+            .collect::<Vec<_>>();
+        err.multipart_suggestion(
+            "if you change the return type to expect trait objects, box the returned expressions",
+            sugg,
+            Applicability::MaybeIncorrect,
+        );
+    }
+
+    /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
+    /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
+    /// populate `other_value` with `other_ty`.
+    ///
+    /// ```text
+    /// Foo<Bar<Qux>>
+    /// ^^^^--------^ this is highlighted
+    /// |   |
+    /// |   this type argument is exactly the same as the other type, not highlighted
+    /// this is highlighted
+    /// Bar<Qux>
+    /// -------- this type is the same as a type argument in the other type, not highlighted
+    /// ```
+    fn highlight_outer(
+        &self,
+        value: &mut DiagnosticStyledString,
+        other_value: &mut DiagnosticStyledString,
+        name: String,
+        sub: ty::subst::SubstsRef<'tcx>,
+        pos: usize,
+        other_ty: Ty<'tcx>,
+    ) {
+        // `value` and `other_value` hold two incomplete type representation for display.
+        // `name` is the path of both types being compared. `sub`
+        value.push_highlighted(name);
+        let len = sub.len();
+        if len > 0 {
+            value.push_highlighted("<");
+        }
+
+        // Output the lifetimes for the first type
+        let lifetimes = sub
+            .regions()
+            .map(|lifetime| {
+                let s = lifetime.to_string();
+                if s.is_empty() { "'_".to_string() } else { s }
+            })
+            .collect::<Vec<_>>()
+            .join(", ");
+        if !lifetimes.is_empty() {
+            if sub.regions().count() < len {
+                value.push_normal(lifetimes + ", ");
+            } else {
+                value.push_normal(lifetimes);
+            }
+        }
+
+        // Highlight all the type arguments that aren't at `pos` and compare the type argument at
+        // `pos` and `other_ty`.
+        for (i, type_arg) in sub.types().enumerate() {
+            if i == pos {
+                let values = self.cmp(type_arg, other_ty);
+                value.0.extend((values.0).0);
+                other_value.0.extend((values.1).0);
+            } else {
+                value.push_highlighted(type_arg.to_string());
+            }
+
+            if len > 0 && i != len - 1 {
+                value.push_normal(", ");
+            }
+        }
+        if len > 0 {
+            value.push_highlighted(">");
+        }
+    }
+
+    /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`,
+    /// as that is the difference to the other type.
+    ///
+    /// For the following code:
+    ///
+    /// ```no_run
+    /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>();
+    /// ```
+    ///
+    /// The type error output will behave in the following way:
+    ///
+    /// ```text
+    /// Foo<Bar<Qux>>
+    /// ^^^^--------^ this is highlighted
+    /// |   |
+    /// |   this type argument is exactly the same as the other type, not highlighted
+    /// this is highlighted
+    /// Bar<Qux>
+    /// -------- this type is the same as a type argument in the other type, not highlighted
+    /// ```
+    fn cmp_type_arg(
+        &self,
+        mut t1_out: &mut DiagnosticStyledString,
+        mut t2_out: &mut DiagnosticStyledString,
+        path: String,
+        sub: ty::subst::SubstsRef<'tcx>,
+        other_path: String,
+        other_ty: Ty<'tcx>,
+    ) -> Option<()> {
+        for (i, ta) in sub.types().enumerate() {
+            if ta == other_ty {
+                self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty);
+                return Some(());
+            }
+            if let ty::Adt(def, _) = ta.kind() {
+                let path_ = self.tcx.def_path_str(def.did);
+                if path_ == other_path {
+                    self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty);
+                    return Some(());
+                }
+            }
+        }
+        None
+    }
+
+    /// Adds a `,` to the type representation only if it is appropriate.
+    fn push_comma(
+        &self,
+        value: &mut DiagnosticStyledString,
+        other_value: &mut DiagnosticStyledString,
+        len: usize,
+        pos: usize,
+    ) {
+        if len > 0 && pos != len - 1 {
+            value.push_normal(", ");
+            other_value.push_normal(", ");
+        }
+    }
+
+    /// For generic types with parameters with defaults, remove the parameters corresponding to
+    /// the defaults. This repeats a lot of the logic found in `ty::print::pretty`.
+    fn strip_generic_default_params(
+        &self,
+        def_id: DefId,
+        substs: ty::subst::SubstsRef<'tcx>,
+    ) -> SubstsRef<'tcx> {
+        let generics = self.tcx.generics_of(def_id);
+        let mut num_supplied_defaults = 0;
+
+        let default_params = generics.params.iter().rev().filter_map(|param| match param.kind {
+            ty::GenericParamDefKind::Type { has_default: true, .. } => Some(param.def_id),
+            ty::GenericParamDefKind::Const { has_default: true } => Some(param.def_id),
+            _ => None,
+        });
+        for (def_id, actual) in iter::zip(default_params, substs.iter().rev()) {
+            match actual.unpack() {
+                GenericArgKind::Const(c) => {
+                    if self.tcx.const_param_default(def_id).subst(self.tcx, substs) != c {
+                        break;
+                    }
+                }
+                GenericArgKind::Type(ty) => {
+                    if self.tcx.type_of(def_id).subst(self.tcx, substs) != ty {
+                        break;
+                    }
+                }
+                _ => break,
+            }
+            num_supplied_defaults += 1;
+        }
+        let len = generics.params.len();
+        let mut generics = generics.clone();
+        generics.params.truncate(len - num_supplied_defaults);
+        substs.truncate_to(self.tcx, &generics)
+    }
+
+    /// Given two `fn` signatures highlight only sub-parts that are different.
+    fn cmp_fn_sig(
+        &self,
+        sig1: &ty::PolyFnSig<'tcx>,
+        sig2: &ty::PolyFnSig<'tcx>,
+    ) -> (DiagnosticStyledString, DiagnosticStyledString) {
+        let get_lifetimes = |sig| {
+            use rustc_hir::def::Namespace;
+            let mut s = String::new();
+            let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS)
+                .name_all_regions(sig)
+                .unwrap();
+            let lts: Vec<String> = reg.into_iter().map(|(_, kind)| kind.to_string()).collect();
+            (if lts.is_empty() { String::new() } else { format!("for<{}> ", lts.join(", ")) }, sig)
+        };
+
+        let (lt1, sig1) = get_lifetimes(sig1);
+        let (lt2, sig2) = get_lifetimes(sig2);
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        let mut values = (
+            DiagnosticStyledString::normal("".to_string()),
+            DiagnosticStyledString::normal("".to_string()),
+        );
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        // ^^^^^^
+        values.0.push(sig1.unsafety.prefix_str(), sig1.unsafety != sig2.unsafety);
+        values.1.push(sig2.unsafety.prefix_str(), sig1.unsafety != sig2.unsafety);
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //        ^^^^^^^^^^
+        if sig1.abi != abi::Abi::Rust {
+            values.0.push(format!("extern {} ", sig1.abi), sig1.abi != sig2.abi);
+        }
+        if sig2.abi != abi::Abi::Rust {
+            values.1.push(format!("extern {} ", sig2.abi), sig1.abi != sig2.abi);
+        }
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                   ^^^^^^^^
+        let lifetime_diff = lt1 != lt2;
+        values.0.push(lt1, lifetime_diff);
+        values.1.push(lt2, lifetime_diff);
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                           ^^^
+        values.0.push_normal("fn(");
+        values.1.push_normal("fn(");
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                              ^^^^^
+        let len1 = sig1.inputs().len();
+        let len2 = sig2.inputs().len();
+        if len1 == len2 {
+            for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() {
+                let (x1, x2) = self.cmp(l, r);
+                (values.0).0.extend(x1.0);
+                (values.1).0.extend(x2.0);
+                self.push_comma(&mut values.0, &mut values.1, len1, i);
+            }
+        } else {
+            for (i, l) in sig1.inputs().iter().enumerate() {
+                values.0.push_highlighted(l.to_string());
+                if i != len1 - 1 {
+                    values.0.push_highlighted(", ");
+                }
+            }
+            for (i, r) in sig2.inputs().iter().enumerate() {
+                values.1.push_highlighted(r.to_string());
+                if i != len2 - 1 {
+                    values.1.push_highlighted(", ");
+                }
+            }
+        }
+
+        if sig1.c_variadic {
+            if len1 > 0 {
+                values.0.push_normal(", ");
+            }
+            values.0.push("...", !sig2.c_variadic);
+        }
+        if sig2.c_variadic {
+            if len2 > 0 {
+                values.1.push_normal(", ");
+            }
+            values.1.push("...", !sig1.c_variadic);
+        }
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                                   ^
+        values.0.push_normal(")");
+        values.1.push_normal(")");
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                                     ^^^^^^^^
+        let output1 = sig1.output();
+        let output2 = sig2.output();
+        let (x1, x2) = self.cmp(output1, output2);
+        if !output1.is_unit() {
+            values.0.push_normal(" -> ");
+            (values.0).0.extend(x1.0);
+        }
+        if !output2.is_unit() {
+            values.1.push_normal(" -> ");
+            (values.1).0.extend(x2.0);
+        }
+        values
+    }
+
+    /// Compares two given types, eliding parts that are the same between them and highlighting
+    /// relevant differences, and return two representation of those types for highlighted printing.
+    fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) {
+        debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind());
+
+        // helper functions
+        fn equals<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+            match (a.kind(), b.kind()) {
+                (a, b) if *a == *b => true,
+                (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_)))
+                | (
+                    &ty::Infer(ty::InferTy::IntVar(_)),
+                    &ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_)),
+                )
+                | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_)))
+                | (
+                    &ty::Infer(ty::InferTy::FloatVar(_)),
+                    &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)),
+                ) => true,
+                _ => false,
+            }
+        }
+
+        fn push_ty_ref<'tcx>(
+            region: &ty::Region<'tcx>,
+            ty: Ty<'tcx>,
+            mutbl: hir::Mutability,
+            s: &mut DiagnosticStyledString,
+        ) {
+            let mut r = region.to_string();
+            if r == "'_" {
+                r.clear();
+            } else {
+                r.push(' ');
+            }
+            s.push_highlighted(format!("&{}{}", r, mutbl.prefix_str()));
+            s.push_normal(ty.to_string());
+        }
+
+        // process starts here
+        match (t1.kind(), t2.kind()) {
+            (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => {
+                let sub_no_defaults_1 = self.strip_generic_default_params(def1.did, sub1);
+                let sub_no_defaults_2 = self.strip_generic_default_params(def2.did, sub2);
+                let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
+                let path1 = self.tcx.def_path_str(def1.did);
+                let path2 = self.tcx.def_path_str(def2.did);
+                if def1.did == def2.did {
+                    // Easy case. Replace same types with `_` to shorten the output and highlight
+                    // the differing ones.
+                    //     let x: Foo<Bar, Qux> = y::<Foo<Quz, Qux>>();
+                    //     Foo<Bar, _>
+                    //     Foo<Quz, _>
+                    //         ---  ^ type argument elided
+                    //         |
+                    //         highlighted in output
+                    values.0.push_normal(path1);
+                    values.1.push_normal(path2);
+
+                    // Avoid printing out default generic parameters that are common to both
+                    // types.
+                    let len1 = sub_no_defaults_1.len();
+                    let len2 = sub_no_defaults_2.len();
+                    let common_len = cmp::min(len1, len2);
+                    let remainder1: Vec<_> = sub1.types().skip(common_len).collect();
+                    let remainder2: Vec<_> = sub2.types().skip(common_len).collect();
+                    let common_default_params =
+                        iter::zip(remainder1.iter().rev(), remainder2.iter().rev())
+                            .filter(|(a, b)| a == b)
+                            .count();
+                    let len = sub1.len() - common_default_params;
+                    let consts_offset = len - sub1.consts().count();
+
+                    // Only draw `<...>` if there're lifetime/type arguments.
+                    if len > 0 {
+                        values.0.push_normal("<");
+                        values.1.push_normal("<");
+                    }
+
+                    fn lifetime_display(lifetime: Region<'_>) -> String {
+                        let s = lifetime.to_string();
+                        if s.is_empty() { "'_".to_string() } else { s }
+                    }
+                    // At one point we'd like to elide all lifetimes here, they are irrelevant for
+                    // all diagnostics that use this output
+                    //
+                    //     Foo<'x, '_, Bar>
+                    //     Foo<'y, '_, Qux>
+                    //         ^^  ^^  --- type arguments are not elided
+                    //         |   |
+                    //         |   elided as they were the same
+                    //         not elided, they were different, but irrelevant
+                    let lifetimes = sub1.regions().zip(sub2.regions());
+                    for (i, lifetimes) in lifetimes.enumerate() {
+                        let l1 = lifetime_display(lifetimes.0);
+                        let l2 = lifetime_display(lifetimes.1);
+                        if lifetimes.0 == lifetimes.1 {
+                            values.0.push_normal("'_");
+                            values.1.push_normal("'_");
+                        } else {
+                            values.0.push_highlighted(l1);
+                            values.1.push_highlighted(l2);
+                        }
+                        self.push_comma(&mut values.0, &mut values.1, len, i);
+                    }
+
+                    // We're comparing two types with the same path, so we compare the type
+                    // arguments for both. If they are the same, do not highlight and elide from the
+                    // output.
+                    //     Foo<_, Bar>
+                    //     Foo<_, Qux>
+                    //         ^ elided type as this type argument was the same in both sides
+                    let type_arguments = sub1.types().zip(sub2.types());
+                    let regions_len = sub1.regions().count();
+                    let num_display_types = consts_offset - regions_len;
+                    for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() {
+                        let i = i + regions_len;
+                        if ta1 == ta2 {
+                            values.0.push_normal("_");
+                            values.1.push_normal("_");
+                        } else {
+                            let (x1, x2) = self.cmp(ta1, ta2);
+                            (values.0).0.extend(x1.0);
+                            (values.1).0.extend(x2.0);
+                        }
+                        self.push_comma(&mut values.0, &mut values.1, len, i);
+                    }
+
+                    // Do the same for const arguments, if they are equal, do not highlight and
+                    // elide them from the output.
+                    let const_arguments = sub1.consts().zip(sub2.consts());
+                    for (i, (ca1, ca2)) in const_arguments.enumerate() {
+                        let i = i + consts_offset;
+                        if ca1 == ca2 {
+                            values.0.push_normal("_");
+                            values.1.push_normal("_");
+                        } else {
+                            values.0.push_highlighted(ca1.to_string());
+                            values.1.push_highlighted(ca2.to_string());
+                        }
+                        self.push_comma(&mut values.0, &mut values.1, len, i);
+                    }
+
+                    // Close the type argument bracket.
+                    // Only draw `<...>` if there're lifetime/type arguments.
+                    if len > 0 {
+                        values.0.push_normal(">");
+                        values.1.push_normal(">");
+                    }
+                    values
+                } else {
+                    // Check for case:
+                    //     let x: Foo<Bar<Qux> = foo::<Bar<Qux>>();
+                    //     Foo<Bar<Qux>
+                    //         ------- this type argument is exactly the same as the other type
+                    //     Bar<Qux>
+                    if self
+                        .cmp_type_arg(
+                            &mut values.0,
+                            &mut values.1,
+                            path1.clone(),
+                            sub_no_defaults_1,
+                            path2.clone(),
+                            &t2,
+                        )
+                        .is_some()
+                    {
+                        return values;
+                    }
+                    // Check for case:
+                    //     let x: Bar<Qux> = y:<Foo<Bar<Qux>>>();
+                    //     Bar<Qux>
+                    //     Foo<Bar<Qux>>
+                    //         ------- this type argument is exactly the same as the other type
+                    if self
+                        .cmp_type_arg(
+                            &mut values.1,
+                            &mut values.0,
+                            path2,
+                            sub_no_defaults_2,
+                            path1,
+                            &t1,
+                        )
+                        .is_some()
+                    {
+                        return values;
+                    }
+
+                    // We can't find anything in common, highlight relevant part of type path.
+                    //     let x: foo::bar::Baz<Qux> = y:<foo::bar::Bar<Zar>>();
+                    //     foo::bar::Baz<Qux>
+                    //     foo::bar::Bar<Zar>
+                    //               -------- this part of the path is different
+
+                    let t1_str = t1.to_string();
+                    let t2_str = t2.to_string();
+                    let min_len = t1_str.len().min(t2_str.len());
+
+                    const SEPARATOR: &str = "::";
+                    let separator_len = SEPARATOR.len();
+                    let split_idx: usize =
+                        iter::zip(t1_str.split(SEPARATOR), t2_str.split(SEPARATOR))
+                            .take_while(|(mod1_str, mod2_str)| mod1_str == mod2_str)
+                            .map(|(mod_str, _)| mod_str.len() + separator_len)
+                            .sum();
+
+                    debug!(
+                        "cmp: separator_len={}, split_idx={}, min_len={}",
+                        separator_len, split_idx, min_len
+                    );
+
+                    if split_idx >= min_len {
+                        // paths are identical, highlight everything
+                        (
+                            DiagnosticStyledString::highlighted(t1_str),
+                            DiagnosticStyledString::highlighted(t2_str),
+                        )
+                    } else {
+                        let (common, uniq1) = t1_str.split_at(split_idx);
+                        let (_, uniq2) = t2_str.split_at(split_idx);
+                        debug!("cmp: common={}, uniq1={}, uniq2={}", common, uniq1, uniq2);
+
+                        values.0.push_normal(common);
+                        values.0.push_highlighted(uniq1);
+                        values.1.push_normal(common);
+                        values.1.push_highlighted(uniq2);
+
+                        values
+                    }
+                }
+            }
+
+            // When finding T != &T, highlight only the borrow
+            (&ty::Ref(r1, ref_ty1, mutbl1), _) if equals(&ref_ty1, &t2) => {
+                let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
+                push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0);
+                values.1.push_normal(t2.to_string());
+                values
+            }
+            (_, &ty::Ref(r2, ref_ty2, mutbl2)) if equals(&t1, &ref_ty2) => {
+                let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
+                values.0.push_normal(t1.to_string());
+                push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1);
+                values
+            }
+
+            // When encountering &T != &mut T, highlight only the borrow
+            (&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2))
+                if equals(&ref_ty1, &ref_ty2) =>
+            {
+                let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
+                push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0);
+                push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1);
+                values
+            }
+
+            // When encountering tuples of the same size, highlight only the differing types
+            (&ty::Tuple(substs1), &ty::Tuple(substs2)) if substs1.len() == substs2.len() => {
+                let mut values =
+                    (DiagnosticStyledString::normal("("), DiagnosticStyledString::normal("("));
+                let len = substs1.len();
+                for (i, (left, right)) in substs1.types().zip(substs2.types()).enumerate() {
+                    let (x1, x2) = self.cmp(left, right);
+                    (values.0).0.extend(x1.0);
+                    (values.1).0.extend(x2.0);
+                    self.push_comma(&mut values.0, &mut values.1, len, i);
+                }
+                if len == 1 {
+                    // Keep the output for single element tuples as `(ty,)`.
+                    values.0.push_normal(",");
+                    values.1.push_normal(",");
+                }
+                values.0.push_normal(")");
+                values.1.push_normal(")");
+                values
+            }
+
+            (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
+                let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+                let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+                let mut values = self.cmp_fn_sig(&sig1, &sig2);
+                let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1));
+                let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did2, substs2));
+                let same_path = path1 == path2;
+                values.0.push(path1, !same_path);
+                values.1.push(path2, !same_path);
+                values
+            }
+
+            (ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => {
+                let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+                let mut values = self.cmp_fn_sig(&sig1, sig2);
+                values.0.push_highlighted(format!(
+                    " {{{}}}",
+                    self.tcx.def_path_str_with_substs(*did1, substs1)
+                ));
+                values
+            }
+
+            (ty::FnPtr(sig1), ty::FnDef(did2, substs2)) => {
+                let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+                let mut values = self.cmp_fn_sig(sig1, &sig2);
+                values.1.push_normal(format!(
+                    " {{{}}}",
+                    self.tcx.def_path_str_with_substs(*did2, substs2)
+                ));
+                values
+            }
+
+            (ty::FnPtr(sig1), ty::FnPtr(sig2)) => self.cmp_fn_sig(sig1, sig2),
+
+            _ => {
+                if t1 == t2 {
+                    // The two types are the same, elide and don't highlight.
+                    (DiagnosticStyledString::normal("_"), DiagnosticStyledString::normal("_"))
+                } else {
+                    // We couldn't find anything in common, highlight everything.
+                    (
+                        DiagnosticStyledString::highlighted(t1.to_string()),
+                        DiagnosticStyledString::highlighted(t2.to_string()),
+                    )
+                }
+            }
+        }
+    }
+
+    pub fn note_type_err(
+        &self,
+        diag: &mut DiagnosticBuilder<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        secondary_span: Option<(Span, String)>,
+        mut values: Option<ValuePairs<'tcx>>,
+        terr: &TypeError<'tcx>,
+    ) {
+        let span = cause.span(self.tcx);
+        debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr);
+
+        // For some types of errors, expected-found does not make
+        // sense, so just ignore the values we were given.
+        if let TypeError::CyclicTy(_) = terr {
+            values = None;
+        }
+        struct OpaqueTypesVisitor<'tcx> {
+            types: FxHashMap<TyCategory, FxHashSet<Span>>,
+            expected: FxHashMap<TyCategory, FxHashSet<Span>>,
+            found: FxHashMap<TyCategory, FxHashSet<Span>>,
+            ignore_span: Span,
+            tcx: TyCtxt<'tcx>,
+        }
+
+        impl<'tcx> OpaqueTypesVisitor<'tcx> {
+            fn visit_expected_found(
+                tcx: TyCtxt<'tcx>,
+                expected: Ty<'tcx>,
+                found: Ty<'tcx>,
+                ignore_span: Span,
+            ) -> Self {
+                let mut types_visitor = OpaqueTypesVisitor {
+                    types: Default::default(),
+                    expected: Default::default(),
+                    found: Default::default(),
+                    ignore_span,
+                    tcx,
+                };
+                // The visitor puts all the relevant encountered types in `self.types`, but in
+                // here we want to visit two separate types with no relation to each other, so we
+                // move the results from `types` to `expected` or `found` as appropriate.
+                expected.visit_with(&mut types_visitor);
+                std::mem::swap(&mut types_visitor.expected, &mut types_visitor.types);
+                found.visit_with(&mut types_visitor);
+                std::mem::swap(&mut types_visitor.found, &mut types_visitor.types);
+                types_visitor
+            }
+
+            fn report(&self, err: &mut DiagnosticBuilder<'_>) {
+                self.add_labels_for_types(err, "expected", &self.expected);
+                self.add_labels_for_types(err, "found", &self.found);
+            }
+
+            fn add_labels_for_types(
+                &self,
+                err: &mut DiagnosticBuilder<'_>,
+                target: &str,
+                types: &FxHashMap<TyCategory, FxHashSet<Span>>,
+            ) {
+                for (key, values) in types.iter() {
+                    let count = values.len();
+                    let kind = key.descr();
+                    let mut returned_async_output_error = false;
+                    for &sp in values {
+                        if sp.is_desugaring(DesugaringKind::Async) && !returned_async_output_error {
+                            if &[sp] != err.span.primary_spans() {
+                                let mut span: MultiSpan = sp.into();
+                                span.push_span_label(
+                                    sp,
+                                    format!(
+                                        "checked the `Output` of this `async fn`, {}{} {}{}",
+                                        if count > 1 { "one of the " } else { "" },
+                                        target,
+                                        kind,
+                                        pluralize!(count),
+                                    ),
+                                );
+                                err.span_note(
+                                    span,
+                                    "while checking the return type of the `async fn`",
+                                );
+                            } else {
+                                err.span_label(
+                                    sp,
+                                    format!(
+                                        "checked the `Output` of this `async fn`, {}{} {}{}",
+                                        if count > 1 { "one of the " } else { "" },
+                                        target,
+                                        kind,
+                                        pluralize!(count),
+                                    ),
+                                );
+                                err.note("while checking the return type of the `async fn`");
+                            }
+                            returned_async_output_error = true;
+                        } else {
+                            err.span_label(
+                                sp,
+                                format!(
+                                    "{}{} {}{}",
+                                    if count == 1 { "the " } else { "one of the " },
+                                    target,
+                                    kind,
+                                    pluralize!(count),
+                                ),
+                            );
+                        }
+                    }
+                }
+            }
+        }
+
+        impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> {
+            fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+                if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) {
+                    let span = self.tcx.def_span(def_id);
+                    // Avoid cluttering the output when the "found" and error span overlap:
+                    //
+                    // error[E0308]: mismatched types
+                    //   --> $DIR/issue-20862.rs:2:5
+                    //    |
+                    // LL |     |y| x + y
+                    //    |     ^^^^^^^^^
+                    //    |     |
+                    //    |     the found closure
+                    //    |     expected `()`, found closure
+                    //    |
+                    //    = note: expected unit type `()`
+                    //                 found closure `[closure@$DIR/issue-20862.rs:2:5: 2:14 x:_]`
+                    if !self.ignore_span.overlaps(span) {
+                        self.types.entry(kind).or_default().insert(span);
+                    }
+                }
+                t.super_visit_with(self)
+            }
+        }
+
+        debug!("note_type_err(diag={:?})", diag);
+        enum Mismatch<'a> {
+            Variable(ty::error::ExpectedFound<Ty<'a>>),
+            Fixed(&'static str),
+        }
+        let (expected_found, exp_found, is_simple_error) = match values {
+            None => (None, Mismatch::Fixed("type"), false),
+            Some(values) => {
+                let (is_simple_error, exp_found) = match values {
+                    ValuePairs::Types(exp_found) => {
+                        let is_simple_err =
+                            exp_found.expected.is_simple_text() && exp_found.found.is_simple_text();
+                        OpaqueTypesVisitor::visit_expected_found(
+                            self.tcx,
+                            exp_found.expected,
+                            exp_found.found,
+                            span,
+                        )
+                        .report(diag);
+
+                        (is_simple_err, Mismatch::Variable(exp_found))
+                    }
+                    ValuePairs::TraitRefs(_) => (false, Mismatch::Fixed("trait")),
+                    _ => (false, Mismatch::Fixed("type")),
+                };
+                let vals = match self.values_str(values) {
+                    Some((expected, found)) => Some((expected, found)),
+                    None => {
+                        // Derived error. Cancel the emitter.
+                        diag.cancel();
+                        return;
+                    }
+                };
+                (vals, exp_found, is_simple_error)
+            }
+        };
+
+        // Ignore msg for object safe coercion
+        // since E0038 message will be printed
+        match terr {
+            TypeError::ObjectUnsafeCoercion(_) => {}
+            _ => {
+                diag.span_label(span, terr.to_string());
+                if let Some((sp, msg)) = secondary_span {
+                    diag.span_label(sp, msg);
+                }
+            }
+        };
+        if let Some((expected, found)) = expected_found {
+            let (expected_label, found_label, exp_found) = match exp_found {
+                Mismatch::Variable(ef) => (
+                    ef.expected.prefix_string(self.tcx),
+                    ef.found.prefix_string(self.tcx),
+                    Some(ef),
+                ),
+                Mismatch::Fixed(s) => (s.into(), s.into(), None),
+            };
+            match (&terr, expected == found) {
+                (TypeError::Sorts(values), extra) => {
+                    let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
+                        (true, ty::Opaque(def_id, _)) => {
+                            let pos = self
+                                .tcx
+                                .sess
+                                .source_map()
+                                .lookup_char_pos(self.tcx.def_span(*def_id).lo());
+                            format!(
+                                " (opaque type at <{}:{}:{}>)",
+                                pos.file.name.prefer_local(),
+                                pos.line,
+                                pos.col.to_usize() + 1,
+                            )
+                        }
+                        (true, _) => format!(" ({})", ty.sort_string(self.tcx)),
+                        (false, _) => "".to_string(),
+                    };
+                    if !(values.expected.is_simple_text() && values.found.is_simple_text())
+                        || (exp_found.map_or(false, |ef| {
+                            // This happens when the type error is a subset of the expectation,
+                            // like when you have two references but one is `usize` and the other
+                            // is `f32`. In those cases we still want to show the `note`. If the
+                            // value from `ef` is `Infer(_)`, then we ignore it.
+                            if !ef.expected.is_ty_infer() {
+                                ef.expected != values.expected
+                            } else if !ef.found.is_ty_infer() {
+                                ef.found != values.found
+                            } else {
+                                false
+                            }
+                        }))
+                    {
+                        diag.note_expected_found_extra(
+                            &expected_label,
+                            expected,
+                            &found_label,
+                            found,
+                            &sort_string(values.expected),
+                            &sort_string(values.found),
+                        );
+                    }
+                }
+                (TypeError::ObjectUnsafeCoercion(_), _) => {
+                    diag.note_unsuccessful_coercion(found, expected);
+                }
+                (_, _) => {
+                    debug!(
+                        "note_type_err: exp_found={:?}, expected={:?} found={:?}",
+                        exp_found, expected, found
+                    );
+                    if !is_simple_error || terr.must_include_note() {
+                        diag.note_expected_found(&expected_label, expected, &found_label, found);
+                    }
+                }
+            }
+        }
+        let exp_found = match exp_found {
+            Mismatch::Variable(exp_found) => Some(exp_found),
+            Mismatch::Fixed(_) => None,
+        };
+        let exp_found = match terr {
+            // `terr` has more accurate type information than `exp_found` in match expressions.
+            ty::error::TypeError::Sorts(terr)
+                if exp_found.map_or(false, |ef| terr.found == ef.found) =>
+            {
+                Some(*terr)
+            }
+            _ => exp_found,
+        };
+        debug!("exp_found {:?} terr {:?}", exp_found, terr);
+        if let Some(exp_found) = exp_found {
+            self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
+            self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
+            self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
+        }
+
+        // In some (most?) cases cause.body_id points to actual body, but in some cases
+        // it's a actual definition. According to the comments (e.g. in
+        // librustc_typeck/check/compare_method.rs:compare_predicate_entailment) the latter
+        // is relied upon by some other code. This might (or might not) need cleanup.
+        let body_owner_def_id =
+            self.tcx.hir().opt_local_def_id(cause.body_id).unwrap_or_else(|| {
+                self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: cause.body_id })
+            });
+        self.check_and_note_conflicting_crates(diag, terr);
+        self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id());
+
+        if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values {
+            if let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() {
+                if let Some(def_id) = def_id.as_local() {
+                    let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+                    let span = self.tcx.hir().span(hir_id);
+                    diag.span_note(span, "this closure does not fulfill the lifetime requirements");
+                }
+            }
+        }
+
+        // It reads better to have the error origin as the final
+        // thing.
+        self.note_error_origin(diag, cause, exp_found);
+    }
+
+    pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+        if let ty::Opaque(def_id, substs) = ty.kind() {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+            // Future::Output
+            let item_def_id = self
+                .tcx
+                .associated_items(future_trait)
+                .in_definition_order()
+                .next()
+                .unwrap()
+                .def_id;
+
+            let bounds = self.tcx.explicit_item_bounds(*def_id);
+
+            for (predicate, _) in bounds {
+                let predicate = predicate.subst(self.tcx, substs);
+                if let ty::PredicateKind::Projection(projection_predicate) =
+                    predicate.kind().skip_binder()
+                {
+                    if projection_predicate.projection_ty.item_def_id == item_def_id {
+                        // We don't account for multiple `Future::Output = Ty` contraints.
+                        return Some(projection_predicate.ty);
+                    }
+                }
+            }
+        }
+        None
+    }
+
+    /// A possible error is to forget to add `.await` when using futures:
+    ///
+    /// ```
+    /// async fn make_u32() -> u32 {
+    ///     22
+    /// }
+    ///
+    /// fn take_u32(x: u32) {}
+    ///
+    /// async fn foo() {
+    ///     let x = make_u32();
+    ///     take_u32(x);
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+    /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+    /// `.await` to the tail of the expression.
+    fn suggest_await_on_expect_found(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut DiagnosticBuilder<'tcx>,
+    ) {
+        debug!(
+            "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
+            exp_span, exp_found.expected, exp_found.found,
+        );
+
+        if let ObligationCauseCode::CompareImplMethodObligation { .. } = &cause.code {
+            return;
+        }
+
+        match (
+            self.get_impl_future_output_ty(exp_found.expected),
+            self.get_impl_future_output_ty(exp_found.found),
+        ) {
+            (Some(exp), Some(found)) if ty::TyS::same_type(exp, found) => match &cause.code {
+                ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => {
+                    diag.multipart_suggestion(
+                        "consider `await`ing on both `Future`s",
+                        vec![
+                            (then.shrink_to_hi(), ".await".to_string()),
+                            (exp_span.shrink_to_hi(), ".await".to_string()),
+                        ],
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+                    prior_arms,
+                    ..
+                }) => {
+                    if let [.., arm_span] = &prior_arms[..] {
+                        diag.multipart_suggestion(
+                            "consider `await`ing on both `Future`s",
+                            vec![
+                                (arm_span.shrink_to_hi(), ".await".to_string()),
+                                (exp_span.shrink_to_hi(), ".await".to_string()),
+                            ],
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        diag.help("consider `await`ing on both `Future`s");
+                    }
+                }
+                _ => {
+                    diag.help("consider `await`ing on both `Future`s");
+                }
+            },
+            (_, Some(ty)) if ty::TyS::same_type(exp_found.expected, ty) => {
+                let span = match cause.code {
+                    // scrutinee's span
+                    ObligationCauseCode::Pattern { span: Some(span), .. } => span,
+                    _ => exp_span,
+                };
+                diag.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    "consider `await`ing on the `Future`",
+                    ".await".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            (Some(ty), _) if ty::TyS::same_type(ty, exp_found.found) => {
+                let span = match cause.code {
+                    // scrutinee's span
+                    ObligationCauseCode::Pattern { span: Some(span), .. } => span,
+                    _ => exp_span,
+                };
+                diag.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    "consider `await`ing on the `Future`",
+                    ".await".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            _ => {}
+        }
+    }
+
+    fn suggest_accessing_field_where_appropriate(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut DiagnosticBuilder<'tcx>,
+    ) {
+        debug!(
+            "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
+            cause, exp_found
+        );
+        if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
+            if expected_def.is_enum() {
+                return;
+            }
+
+            if let Some((name, ty)) = expected_def
+                .non_enum_variant()
+                .fields
+                .iter()
+                .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
+                .map(|field| (field.ident.name, field.ty(self.tcx, expected_substs)))
+                .find(|(_, ty)| ty::TyS::same_type(ty, exp_found.found))
+            {
+                if let ObligationCauseCode::Pattern { span: Some(span), .. } = cause.code {
+                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                        let suggestion = if expected_def.is_struct() {
+                            format!("{}.{}", snippet, name)
+                        } else if expected_def.is_union() {
+                            format!("unsafe {{ {}.{} }}", snippet, name)
+                        } else {
+                            return;
+                        };
+                        diag.span_suggestion(
+                            span,
+                            &format!(
+                                "you might have meant to use field `{}` whose type is `{}`",
+                                name, ty
+                            ),
+                            suggestion,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
+    /// suggests it.
+    fn suggest_as_ref_where_appropriate(
+        &self,
+        span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut DiagnosticBuilder<'tcx>,
+    ) {
+        if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
+            (exp_found.expected.kind(), exp_found.found.kind())
+        {
+            if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
+                let path_str = format!("{:?}", exp_def);
+                if exp_def == &found_def {
+                    let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \
+                                       `.as_ref()`";
+                    let result_msg = "you can convert from `&Result<T, E>` to \
+                                          `Result<&T, &E>` using `.as_ref()`";
+                    let have_as_ref = &[
+                        ("std::option::Option", opt_msg),
+                        ("core::option::Option", opt_msg),
+                        ("std::result::Result", result_msg),
+                        ("core::result::Result", result_msg),
+                    ];
+                    if let Some(msg) = have_as_ref
+                        .iter()
+                        .find_map(|(path, msg)| (&path_str == path).then_some(msg))
+                    {
+                        let mut show_suggestion = true;
+                        for (exp_ty, found_ty) in
+                            iter::zip(exp_substs.types(), found_substs.types())
+                        {
+                            match *exp_ty.kind() {
+                                ty::Ref(_, exp_ty, _) => {
+                                    match (exp_ty.kind(), found_ty.kind()) {
+                                        (_, ty::Param(_))
+                                        | (_, ty::Infer(_))
+                                        | (ty::Param(_), _)
+                                        | (ty::Infer(_), _) => {}
+                                        _ if ty::TyS::same_type(exp_ty, found_ty) => {}
+                                        _ => show_suggestion = false,
+                                    };
+                                }
+                                ty::Param(_) | ty::Infer(_) => {}
+                                _ => show_suggestion = false,
+                            }
+                        }
+                        if let (Ok(snippet), true) =
+                            (self.tcx.sess.source_map().span_to_snippet(span), show_suggestion)
+                        {
+                            diag.span_suggestion(
+                                span,
+                                msg,
+                                format!("{}.as_ref()", snippet),
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    pub fn report_and_explain_type_error(
+        &self,
+        trace: TypeTrace<'tcx>,
+        terr: &TypeError<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
+
+        let span = trace.cause.span(self.tcx);
+        let failure_code = trace.cause.as_failure_code(terr);
+        let mut diag = match failure_code {
+            FailureCode::Error0038(did) => {
+                let violations = self.tcx.object_safety_violations(did);
+                report_object_safety_error(self.tcx, span, did, violations)
+            }
+            FailureCode::Error0317(failure_str) => {
+                struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str)
+            }
+            FailureCode::Error0580(failure_str) => {
+                struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str)
+            }
+            FailureCode::Error0308(failure_str) => {
+                let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str);
+                if let ValuePairs::Types(ty::error::ExpectedFound { expected, found }) =
+                    trace.values
+                {
+                    // If a tuple of length one was expected and the found expression has
+                    // parentheses around it, perhaps the user meant to write `(expr,)` to
+                    // build a tuple (issue #86100)
+                    match (expected.kind(), found.kind()) {
+                        (ty::Tuple(_), ty::Tuple(_)) => {}
+                        (ty::Tuple(_), _) if expected.tuple_fields().count() == 1 => {
+                            if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
+                                if let Some(code) =
+                                    code.strip_prefix('(').and_then(|s| s.strip_suffix(')'))
+                                {
+                                    err.span_suggestion(
+                                        span,
+                                        "use a trailing comma to create a tuple with one element",
+                                        format!("({},)", code),
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                }
+                            }
+                        }
+                        _ => {}
+                    }
+                }
+                err
+            }
+            FailureCode::Error0644(failure_str) => {
+                struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
+            }
+        };
+        self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
+        diag
+    }
+
+    fn values_str(
+        &self,
+        values: ValuePairs<'tcx>,
+    ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+        match values {
+            infer::Types(exp_found) => self.expected_found_str_ty(exp_found),
+            infer::Regions(exp_found) => self.expected_found_str(exp_found),
+            infer::Consts(exp_found) => self.expected_found_str(exp_found),
+            infer::TraitRefs(exp_found) => {
+                let pretty_exp_found = ty::error::ExpectedFound {
+                    expected: exp_found.expected.print_only_trait_path(),
+                    found: exp_found.found.print_only_trait_path(),
+                };
+                self.expected_found_str(pretty_exp_found)
+            }
+            infer::PolyTraitRefs(exp_found) => {
+                let pretty_exp_found = ty::error::ExpectedFound {
+                    expected: exp_found.expected.print_only_trait_path(),
+                    found: exp_found.found.print_only_trait_path(),
+                };
+                self.expected_found_str(pretty_exp_found)
+            }
+        }
+    }
+
+    fn expected_found_str_ty(
+        &self,
+        exp_found: ty::error::ExpectedFound<Ty<'tcx>>,
+    ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+        let exp_found = self.resolve_vars_if_possible(exp_found);
+        if exp_found.references_error() {
+            return None;
+        }
+
+        Some(self.cmp(exp_found.expected, exp_found.found))
+    }
+
+    /// Returns a string of the form "expected `{}`, found `{}`".
+    fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>(
+        &self,
+        exp_found: ty::error::ExpectedFound<T>,
+    ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+        let exp_found = self.resolve_vars_if_possible(exp_found);
+        if exp_found.references_error() {
+            return None;
+        }
+
+        Some((
+            DiagnosticStyledString::highlighted(exp_found.expected.to_string()),
+            DiagnosticStyledString::highlighted(exp_found.found.to_string()),
+        ))
+    }
+
+    pub fn report_generic_bound_failure(
+        &self,
+        span: Span,
+        origin: Option<SubregionOrigin<'tcx>>,
+        bound_kind: GenericKind<'tcx>,
+        sub: Region<'tcx>,
+    ) {
+        self.construct_generic_bound_failure(span, origin, bound_kind, sub).emit();
+    }
+
+    pub fn construct_generic_bound_failure(
+        &self,
+        span: Span,
+        origin: Option<SubregionOrigin<'tcx>>,
+        bound_kind: GenericKind<'tcx>,
+        sub: Region<'tcx>,
+    ) -> DiagnosticBuilder<'a> {
+        let hir = &self.tcx.hir();
+        // Attempt to obtain the span of the parameter so we can
+        // suggest adding an explicit lifetime bound to it.
+        let generics = self
+            .in_progress_typeck_results
+            .map(|typeck_results| typeck_results.borrow().hir_owner)
+            .map(|owner| {
+                let hir_id = hir.local_def_id_to_hir_id(owner);
+                let parent_id = hir.get_parent_item(hir_id);
+                (
+                    // Parent item could be a `mod`, so we check the HIR before calling:
+                    if let Some(Node::Item(Item {
+                        kind: ItemKind::Trait(..) | ItemKind::Impl { .. },
+                        ..
+                    })) = hir.find(parent_id)
+                    {
+                        Some(self.tcx.generics_of(hir.local_def_id(parent_id).to_def_id()))
+                    } else {
+                        None
+                    },
+                    self.tcx.generics_of(owner.to_def_id()),
+                )
+            });
+        let type_param_span = match (generics, bound_kind) {
+            (Some((_, ref generics)), GenericKind::Param(ref param)) => {
+                // Account for the case where `param` corresponds to `Self`,
+                // which doesn't have the expected type argument.
+                if !(generics.has_self && param.index == 0) {
+                    let type_param = generics.type_param(param, self.tcx);
+                    type_param.def_id.as_local().map(|def_id| {
+                        // Get the `hir::Param` to verify whether it already has any bounds.
+                        // We do this to avoid suggesting code that ends up as `T: 'a'b`,
+                        // instead we suggest `T: 'a + 'b` in that case.
+                        let id = hir.local_def_id_to_hir_id(def_id);
+                        let mut has_bounds = false;
+                        if let Node::GenericParam(param) = hir.get(id) {
+                            has_bounds = !param.bounds.is_empty();
+                        }
+                        let sp = hir.span(id);
+                        // `sp` only covers `T`, change it so that it covers
+                        // `T:` when appropriate
+                        let is_impl_trait = bound_kind.to_string().starts_with("impl ");
+                        let sp = if has_bounds && !is_impl_trait {
+                            sp.to(self
+                                .tcx
+                                .sess
+                                .source_map()
+                                .next_point(self.tcx.sess.source_map().next_point(sp)))
+                        } else {
+                            sp
+                        };
+                        (sp, has_bounds, is_impl_trait)
+                    })
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        };
+        let new_lt = generics
+            .as_ref()
+            .and_then(|(parent_g, g)| {
+                let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
+                let mut lts_names = g
+                    .params
+                    .iter()
+                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
+                    .map(|p| p.name.as_str())
+                    .collect::<Vec<_>>();
+                if let Some(g) = parent_g {
+                    lts_names.extend(
+                        g.params
+                            .iter()
+                            .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
+                            .map(|p| p.name.as_str()),
+                    );
+                }
+                let lts = lts_names.iter().map(|s| -> &str { &*s }).collect::<Vec<_>>();
+                possible.find(|candidate| !lts.contains(&candidate.as_str()))
+            })
+            .unwrap_or("'lt".to_string());
+        let add_lt_sugg = generics
+            .as_ref()
+            .and_then(|(_, g)| g.params.first())
+            .and_then(|param| param.def_id.as_local())
+            .map(|def_id| {
+                (
+                    hir.span(hir.local_def_id_to_hir_id(def_id)).shrink_to_lo(),
+                    format!("{}, ", new_lt),
+                )
+            });
+
+        let labeled_user_string = match bound_kind {
+            GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
+            GenericKind::Projection(ref p) => format!("the associated type `{}`", p),
+        };
+
+        if let Some(SubregionOrigin::CompareImplMethodObligation {
+            span,
+            item_name,
+            impl_item_def_id,
+            trait_item_def_id,
+        }) = origin
+        {
+            return self.report_extra_impl_obligation(
+                span,
+                item_name,
+                impl_item_def_id,
+                trait_item_def_id,
+                &format!("`{}: {}`", bound_kind, sub),
+            );
+        }
+
+        fn binding_suggestion<'tcx, S: fmt::Display>(
+            err: &mut DiagnosticBuilder<'tcx>,
+            type_param_span: Option<(Span, bool, bool)>,
+            bound_kind: GenericKind<'tcx>,
+            sub: S,
+        ) {
+            let msg = "consider adding an explicit lifetime bound";
+            if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span {
+                let suggestion = if is_impl_trait {
+                    format!("{} + {}", bound_kind, sub)
+                } else {
+                    let tail = if has_lifetimes { " + " } else { "" };
+                    format!("{}: {}{}", bound_kind, sub, tail)
+                };
+                err.span_suggestion(
+                    sp,
+                    &format!("{}...", msg),
+                    suggestion,
+                    Applicability::MaybeIncorrect, // Issue #41966
+                );
+            } else {
+                let consider = format!(
+                    "{} {}...",
+                    msg,
+                    if type_param_span.map_or(false, |(_, _, is_impl_trait)| is_impl_trait) {
+                        format!(" `{}` to `{}`", sub, bound_kind)
+                    } else {
+                        format!("`{}: {}`", bound_kind, sub)
+                    },
+                );
+                err.help(&consider);
+            }
+        }
+
+        let new_binding_suggestion =
+            |err: &mut DiagnosticBuilder<'tcx>,
+             type_param_span: Option<(Span, bool, bool)>,
+             bound_kind: GenericKind<'tcx>| {
+                let msg = "consider introducing an explicit lifetime bound";
+                if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span {
+                    let suggestion = if is_impl_trait {
+                        (sp.shrink_to_hi(), format!(" + {}", new_lt))
+                    } else {
+                        let tail = if has_lifetimes { " +" } else { "" };
+                        (sp, format!("{}: {}{}", bound_kind, new_lt, tail))
+                    };
+                    let mut sugg =
+                        vec![suggestion, (span.shrink_to_hi(), format!(" + {}", new_lt))];
+                    if let Some(lt) = add_lt_sugg {
+                        sugg.push(lt);
+                        sugg.rotate_right(1);
+                    }
+                    // `MaybeIncorrect` due to issue #41966.
+                    err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
+                }
+            };
+
+        let mut err = match *sub {
+            ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. })
+            | ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }) => {
+                // Does the required lifetime have a nice name we can print?
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0309,
+                    "{} may not live long enough",
+                    labeled_user_string
+                );
+                // Explicitly use the name instead of `sub`'s `Display` impl. The `Display` impl
+                // for the bound is not suitable for suggestions when `-Zverbose` is set because it
+                // uses `Debug` output, so we handle it specially here so that suggestions are
+                // always correct.
+                binding_suggestion(&mut err, type_param_span, bound_kind, name);
+                err
+            }
+
+            ty::ReStatic => {
+                // Does the required lifetime have a nice name we can print?
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0310,
+                    "{} may not live long enough",
+                    labeled_user_string
+                );
+                binding_suggestion(&mut err, type_param_span, bound_kind, "'static");
+                err
+            }
+
+            _ => {
+                // If not, be less specific.
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0311,
+                    "{} may not live long enough",
+                    labeled_user_string
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    &format!("{} must be valid for ", labeled_user_string),
+                    sub,
+                    "...",
+                    None,
+                );
+                if let Some(infer::RelateParamBound(_, t, _)) = origin {
+                    let return_impl_trait = self
+                        .in_progress_typeck_results
+                        .map(|typeck_results| typeck_results.borrow().hir_owner)
+                        .and_then(|owner| self.tcx.return_type_impl_trait(owner))
+                        .is_some();
+                    let t = self.resolve_vars_if_possible(t);
+                    match t.kind() {
+                        // We've got:
+                        // fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
+                        // suggest:
+                        // fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
+                        ty::Closure(_, _substs) | ty::Opaque(_, _substs) if return_impl_trait => {
+                            new_binding_suggestion(&mut err, type_param_span, bound_kind);
+                        }
+                        _ => {
+                            binding_suggestion(&mut err, type_param_span, bound_kind, new_lt);
+                        }
+                    }
+                }
+                err
+            }
+        };
+
+        if let Some(origin) = origin {
+            self.note_region_origin(&mut err, &origin);
+        }
+        err
+    }
+
+    fn report_sub_sup_conflict(
+        &self,
+        var_origin: RegionVariableOrigin,
+        sub_origin: SubregionOrigin<'tcx>,
+        sub_region: Region<'tcx>,
+        sup_origin: SubregionOrigin<'tcx>,
+        sup_region: Region<'tcx>,
+    ) {
+        let mut err = self.report_inference_failure(var_origin);
+
+        note_and_explain_region(
+            self.tcx,
+            &mut err,
+            "first, the lifetime cannot outlive ",
+            sup_region,
+            "...",
+            None,
+        );
+
+        debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
+        debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
+        debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
+        debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
+        debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
+
+        if let (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) =
+            (&sup_origin, &sub_origin)
+        {
+            debug!("report_sub_sup_conflict: sup_trace={:?}", sup_trace);
+            debug!("report_sub_sup_conflict: sub_trace={:?}", sub_trace);
+            debug!("report_sub_sup_conflict: sup_trace.values={:?}", sup_trace.values);
+            debug!("report_sub_sup_conflict: sub_trace.values={:?}", sub_trace.values);
+
+            if let (Some((sup_expected, sup_found)), Some((sub_expected, sub_found))) =
+                (self.values_str(sup_trace.values), self.values_str(sub_trace.values))
+            {
+                if sub_expected == sup_expected && sub_found == sup_found {
+                    note_and_explain_region(
+                        self.tcx,
+                        &mut err,
+                        "...but the lifetime must also be valid for ",
+                        sub_region,
+                        "...",
+                        None,
+                    );
+                    err.span_note(
+                        sup_trace.cause.span,
+                        &format!("...so that the {}", sup_trace.cause.as_requirement_str()),
+                    );
+
+                    err.note_expected_found(&"", sup_expected, &"", sup_found);
+                    err.emit();
+                    return;
+                }
+            }
+        }
+
+        self.note_region_origin(&mut err, &sup_origin);
+
+        note_and_explain_region(
+            self.tcx,
+            &mut err,
+            "but, the lifetime must be valid for ",
+            sub_region,
+            "...",
+            None,
+        );
+
+        self.note_region_origin(&mut err, &sub_origin);
+        err.emit();
+    }
+
+    /// Determine whether an error associated with the given span and definition
+    /// should be treated as being caused by the implicit `From` conversion
+    /// within `?` desugaring.
+    pub fn is_try_conversion(&self, span: Span, trait_def_id: DefId) -> bool {
+        span.is_desugaring(DesugaringKind::QuestionMark)
+            && self.tcx.is_diagnostic_item(sym::from_trait, trait_def_id)
+    }
+}
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    fn report_inference_failure(
+        &self,
+        var_origin: RegionVariableOrigin,
+    ) -> DiagnosticBuilder<'tcx> {
+        let br_string = |br: ty::BoundRegionKind| {
+            let mut s = match br {
+                ty::BrNamed(_, name) => name.to_string(),
+                _ => String::new(),
+            };
+            if !s.is_empty() {
+                s.push(' ');
+            }
+            s
+        };
+        let var_description = match var_origin {
+            infer::MiscVariable(_) => String::new(),
+            infer::PatternRegion(_) => " for pattern".to_string(),
+            infer::AddrOfRegion(_) => " for borrow expression".to_string(),
+            infer::Autoref(_, _) => " for autoref".to_string(),
+            infer::Coercion(_) => " for automatic coercion".to_string(),
+            infer::LateBoundRegion(_, br, infer::FnCall) => {
+                format!(" for lifetime parameter {}in function call", br_string(br))
+            }
+            infer::LateBoundRegion(_, br, infer::HigherRankedType) => {
+                format!(" for lifetime parameter {}in generic type", br_string(br))
+            }
+            infer::LateBoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!(
+                " for lifetime parameter {}in trait containing associated type `{}`",
+                br_string(br),
+                self.tcx.associated_item(def_id).ident
+            ),
+            infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name),
+            infer::UpvarRegion(ref upvar_id, _) => {
+                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+                format!(" for capture of `{}` by closure", var_name)
+            }
+            infer::Nll(..) => bug!("NLL variable found in lexical phase"),
+        };
+
+        struct_span_err!(
+            self.tcx.sess,
+            var_origin.span(),
+            E0495,
+            "cannot infer an appropriate lifetime{} due to conflicting requirements",
+            var_description
+        )
+    }
+}
+
+enum FailureCode {
+    Error0038(DefId),
+    Error0317(&'static str),
+    Error0580(&'static str),
+    Error0308(&'static str),
+    Error0644(&'static str),
+}
+
+trait ObligationCauseExt<'tcx> {
+    fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode;
+    fn as_requirement_str(&self) -> &'static str;
+}
+
+impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
+    fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode {
+        use self::FailureCode::*;
+        use crate::traits::ObligationCauseCode::*;
+        match self.code {
+            CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
+            CompareImplTypeObligation { .. } => Error0308("type not compatible with trait"),
+            MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => {
+                Error0308(match source {
+                    hir::MatchSource::IfLetDesugar { .. } => {
+                        "`if let` arms have incompatible types"
+                    }
+                    hir::MatchSource::TryDesugar => {
+                        "try expression alternatives have incompatible types"
+                    }
+                    _ => "`match` arms have incompatible types",
+                })
+            }
+            IfExpression { .. } => Error0308("`if` and `else` have incompatible types"),
+            IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"),
+            MainFunctionType => Error0580("`main` function has wrong type"),
+            StartFunctionType => Error0308("`#[start]` function has wrong type"),
+            IntrinsicType => Error0308("intrinsic has wrong type"),
+            MethodReceiver => Error0308("mismatched `self` parameter type"),
+
+            // In the case where we have no more specific thing to
+            // say, also take a look at the error code, maybe we can
+            // tailor to that.
+            _ => match terr {
+                TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => {
+                    Error0644("closure/generator type that references itself")
+                }
+                TypeError::IntrinsicCast => {
+                    Error0308("cannot coerce intrinsics to function pointers")
+                }
+                TypeError::ObjectUnsafeCoercion(did) => Error0038(*did),
+                _ => Error0308("mismatched types"),
+            },
+        }
+    }
+
+    fn as_requirement_str(&self) -> &'static str {
+        use crate::traits::ObligationCauseCode::*;
+        match self.code {
+            CompareImplMethodObligation { .. } => "method type is compatible with trait",
+            CompareImplTypeObligation { .. } => "associated type is compatible with trait",
+            ExprAssignable => "expression is assignable",
+            MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source {
+                hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types",
+                _ => "`match` arms have compatible types",
+            },
+            IfExpression { .. } => "`if` and `else` have incompatible types",
+            IfExpressionWithNoElse => "`if` missing an `else` returns `()`",
+            MainFunctionType => "`main` function has the correct type",
+            StartFunctionType => "`#[start]` function has the correct type",
+            IntrinsicType => "intrinsic has the correct type",
+            MethodReceiver => "method receiver has the correct type",
+            _ => "types are compatible",
+        }
+    }
+}
+
+/// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks
+/// extra information about each type, but we only care about the category.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub enum TyCategory {
+    Closure,
+    Opaque,
+    Generator(hir::GeneratorKind),
+    Foreign,
+}
+
+impl TyCategory {
+    fn descr(&self) -> &'static str {
+        match self {
+            Self::Closure => "closure",
+            Self::Opaque => "opaque type",
+            Self::Generator(gk) => gk.descr(),
+            Self::Foreign => "foreign type",
+        }
+    }
+
+    pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> {
+        match *ty.kind() {
+            ty::Closure(def_id, _) => Some((Self::Closure, def_id)),
+            ty::Opaque(def_id, _) => Some((Self::Opaque, def_id)),
+            ty::Generator(def_id, ..) => {
+                Some((Self::Generator(tcx.generator_kind(def_id).unwrap()), def_id))
+            }
+            ty::Foreign(def_id) => Some((Self::Foreign, def_id)),
+            _ => None,
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
new file mode 100644
index 00000000000..c2025f3fe4d
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -0,0 +1,858 @@
+use crate::infer::type_variable::TypeVariableOriginKind;
+use crate::infer::InferCtxt;
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Namespace};
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat};
+use rustc_middle::hir::map::Map;
+use rustc_middle::infer::unify_key::ConstVariableOriginKind;
+use rustc_middle::ty::print::Print;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt};
+use rustc_span::source_map::DesugaringKind;
+use rustc_span::symbol::kw;
+use rustc_span::Span;
+use std::borrow::Cow;
+
+struct FindHirNodeVisitor<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    target: GenericArg<'tcx>,
+    target_span: Span,
+    found_node_ty: Option<Ty<'tcx>>,
+    found_local_pattern: Option<&'tcx Pat<'tcx>>,
+    found_arg_pattern: Option<&'tcx Pat<'tcx>>,
+    found_closure: Option<&'tcx Expr<'tcx>>,
+    found_method_call: Option<&'tcx Expr<'tcx>>,
+    found_exact_method_call: Option<&'tcx Expr<'tcx>>,
+    found_use_diagnostic: Option<UseDiagnostic<'tcx>>,
+}
+
+impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
+    fn new(infcx: &'a InferCtxt<'a, 'tcx>, target: GenericArg<'tcx>, target_span: Span) -> Self {
+        Self {
+            infcx,
+            target,
+            target_span,
+            found_node_ty: None,
+            found_local_pattern: None,
+            found_arg_pattern: None,
+            found_closure: None,
+            found_method_call: None,
+            found_exact_method_call: None,
+            found_use_diagnostic: None,
+        }
+    }
+
+    fn node_type_opt(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
+        self.infcx.in_progress_typeck_results?.borrow().node_type_opt(hir_id)
+    }
+
+    fn node_ty_contains_target(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
+        self.node_type_opt(hir_id).map(|ty| self.infcx.resolve_vars_if_possible(ty)).filter(|ty| {
+            ty.walk().any(|inner| {
+                inner == self.target
+                    || match (inner.unpack(), self.target.unpack()) {
+                        (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
+                            use ty::{Infer, TyVar};
+                            match (inner_ty.kind(), target_ty.kind()) {
+                                (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => self
+                                    .infcx
+                                    .inner
+                                    .borrow_mut()
+                                    .type_variables()
+                                    .sub_unified(a_vid, b_vid),
+                                _ => false,
+                            }
+                        }
+                        _ => false,
+                    }
+            })
+        })
+    }
+
+    /// Determine whether the expression, assumed to be the callee within a `Call`,
+    /// corresponds to the `From::from` emitted in desugaring of the `?` operator.
+    fn is_try_conversion(&self, callee: &Expr<'tcx>) -> bool {
+        self.infcx
+            .trait_def_from_hir_fn(callee.hir_id)
+            .map_or(false, |def_id| self.infcx.is_try_conversion(callee.span, def_id))
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.infcx.tcx.hir())
+    }
+
+    fn visit_local(&mut self, local: &'tcx Local<'tcx>) {
+        if let (None, Some(ty)) =
+            (self.found_local_pattern, self.node_ty_contains_target(local.hir_id))
+        {
+            self.found_local_pattern = Some(&*local.pat);
+            self.found_node_ty = Some(ty);
+        }
+        intravisit::walk_local(self, local);
+    }
+
+    fn visit_body(&mut self, body: &'tcx Body<'tcx>) {
+        for param in body.params {
+            if let (None, Some(ty)) =
+                (self.found_arg_pattern, self.node_ty_contains_target(param.hir_id))
+            {
+                self.found_arg_pattern = Some(&*param.pat);
+                self.found_node_ty = Some(ty);
+            }
+        }
+        intravisit::walk_body(self, body);
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        if let ExprKind::MethodCall(_, call_span, exprs, _) = expr.kind {
+            if call_span == self.target_span
+                && Some(self.target)
+                    == self.infcx.in_progress_typeck_results.and_then(|typeck_results| {
+                        typeck_results
+                            .borrow()
+                            .node_type_opt(exprs.first().unwrap().hir_id)
+                            .map(Into::into)
+                    })
+            {
+                self.found_exact_method_call = Some(&expr);
+                return;
+            }
+        }
+
+        // FIXME(const_generics): Currently, any uninferred `const` generics arguments
+        // are handled specially, but instead they should be handled in `annotate_method_call`,
+        // which currently doesn't work because this evaluates to `false` for const arguments.
+        // See https://github.com/rust-lang/rust/pull/77758 for more details.
+        if let Some(ty) = self.node_ty_contains_target(expr.hir_id) {
+            match expr.kind {
+                ExprKind::Closure(..) => self.found_closure = Some(&expr),
+                ExprKind::MethodCall(..) => self.found_method_call = Some(&expr),
+
+                // If the given expression falls within the target span and is a
+                // `From::from(e)` call emitted during desugaring of the `?` operator,
+                // extract the types inferred before and after the call
+                ExprKind::Call(callee, [arg])
+                    if self.target_span.contains(expr.span)
+                        && self.found_use_diagnostic.is_none()
+                        && self.is_try_conversion(callee) =>
+                {
+                    self.found_use_diagnostic = self.node_type_opt(arg.hir_id).map(|pre_ty| {
+                        UseDiagnostic::TryConversion { pre_ty, post_ty: ty, span: callee.span }
+                    });
+                }
+                _ => {}
+            }
+        }
+        intravisit::walk_expr(self, expr);
+    }
+}
+
+/// An observation about the use site of a type to be emitted as an additional
+/// note in an inference failure error.
+enum UseDiagnostic<'tcx> {
+    /// Records the types inferred before and after `From::from` is called on the
+    /// error value within the desugaring of the `?` operator.
+    TryConversion { pre_ty: Ty<'tcx>, post_ty: Ty<'tcx>, span: Span },
+}
+
+impl UseDiagnostic<'_> {
+    /// Return a descriptor of the value at the use site
+    fn descr(&self) -> &'static str {
+        match self {
+            Self::TryConversion { .. } => "error for `?` operator",
+        }
+    }
+
+    /// Return a descriptor of the type at the use site
+    fn type_descr(&self) -> &'static str {
+        match self {
+            Self::TryConversion { .. } => "error type for `?` operator",
+        }
+    }
+
+    fn applies_to(&self, span: Span) -> bool {
+        match *self {
+            // In some cases the span for an inference failure due to try
+            // conversion contains the antecedent expression as well as the `?`
+            Self::TryConversion { span: s, .. } => span.contains(s) && span.hi() == s.hi(),
+        }
+    }
+
+    fn attach_note(&self, err: &mut DiagnosticBuilder<'_>) {
+        match *self {
+            Self::TryConversion { pre_ty, post_ty, .. } => {
+                let intro = "`?` implicitly converts the error value";
+
+                let msg = match (pre_ty.is_ty_infer(), post_ty.is_ty_infer()) {
+                    (true, true) => format!("{} using the `From` trait", intro),
+                    (false, true) => {
+                        format!("{} into a type implementing `From<{}>`", intro, pre_ty)
+                    }
+                    (true, false) => {
+                        format!("{} into `{}` using the `From` trait", intro, post_ty)
+                    }
+                    (false, false) => {
+                        format!(
+                            "{} into `{}` using its implementation of `From<{}>`",
+                            intro, post_ty, pre_ty
+                        )
+                    }
+                };
+
+                err.note(&msg);
+            }
+        }
+    }
+}
+
+/// Suggest giving an appropriate return type to a closure expression.
+fn closure_return_type_suggestion(
+    err: &mut DiagnosticBuilder<'_>,
+    output: &FnRetTy<'_>,
+    body: &Body<'_>,
+    ret: &str,
+) {
+    let (arrow, post) = match output {
+        FnRetTy::DefaultReturn(_) => ("-> ", " "),
+        _ => ("", ""),
+    };
+    let suggestion = match body.value.kind {
+        ExprKind::Block(..) => vec![(output.span(), format!("{}{}{}", arrow, ret, post))],
+        _ => vec![
+            (output.span(), format!("{}{}{}{{ ", arrow, ret, post)),
+            (body.value.span.shrink_to_hi(), " }".to_string()),
+        ],
+    };
+    err.multipart_suggestion(
+        "give this closure an explicit return type without `_` placeholders",
+        suggestion,
+        Applicability::HasPlaceholders,
+    );
+}
+
+/// Given a closure signature, return a `String` containing a list of all its argument types.
+fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String {
+    fn_sig
+        .inputs()
+        .skip_binder()
+        .iter()
+        .next()
+        .map(|args| args.tuple_fields().map(|arg| arg.to_string()).collect::<Vec<_>>().join(", "))
+        .unwrap_or_default()
+}
+
+pub enum TypeAnnotationNeeded {
+    /// ```compile_fail,E0282
+    /// let x = "hello".chars().rev().collect();
+    /// ```
+    E0282,
+    /// An implementation cannot be chosen unambiguously because of lack of information.
+    /// ```compile_fail,E0283
+    /// let _ = Default::default();
+    /// ```
+    E0283,
+    /// ```compile_fail,E0284
+    /// let mut d: u64 = 2;
+    /// d = d % 1u32.into();
+    /// ```
+    E0284,
+}
+
+impl Into<rustc_errors::DiagnosticId> for TypeAnnotationNeeded {
+    fn into(self) -> rustc_errors::DiagnosticId {
+        match self {
+            Self::E0282 => rustc_errors::error_code!(E0282),
+            Self::E0283 => rustc_errors::error_code!(E0283),
+            Self::E0284 => rustc_errors::error_code!(E0284),
+        }
+    }
+}
+
+/// Information about a constant or a type containing inference variables.
+pub struct InferenceDiagnosticsData {
+    pub name: String,
+    pub span: Option<Span>,
+    pub kind: UnderspecifiedArgKind,
+    pub parent: Option<InferenceDiagnosticsParentData>,
+}
+
+/// Data on the parent definition where a generic argument was declared.
+pub struct InferenceDiagnosticsParentData {
+    pub prefix: &'static str,
+    pub name: String,
+    pub def_id: DefId,
+}
+
+pub enum UnderspecifiedArgKind {
+    Type { prefix: Cow<'static, str> },
+    Const { is_parameter: bool },
+}
+
+impl InferenceDiagnosticsData {
+    /// Generate a label for a generic argument which can't be inferred. When not
+    /// much is known about the argument, `use_diag` may be used to describe the
+    /// labeled value.
+    fn cannot_infer_msg(&self, use_diag: Option<&UseDiagnostic<'_>>) -> String {
+        if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) {
+            if let Some(use_diag) = use_diag {
+                return format!("cannot infer type of {}", use_diag.descr());
+            }
+
+            return "cannot infer type".to_string();
+        }
+
+        let suffix = match (&self.parent, use_diag) {
+            (Some(parent), _) => format!(" declared on the {} `{}`", parent.prefix, parent.name),
+            (None, Some(use_diag)) => format!(" in {}", use_diag.type_descr()),
+            (None, None) => String::new(),
+        };
+
+        // For example: "cannot infer type for type parameter `T`"
+        format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
+    }
+}
+
+impl InferenceDiagnosticsParentData {
+    fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
+        let parent_def_id = tcx.parent(def_id)?;
+
+        let parent_name =
+            tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string();
+
+        Some(InferenceDiagnosticsParentData {
+            prefix: tcx.def_kind(parent_def_id).descr(parent_def_id),
+            name: parent_name,
+            def_id: parent_def_id,
+        })
+    }
+}
+
+impl UnderspecifiedArgKind {
+    fn prefix_string(&self) -> Cow<'static, str> {
+        match self {
+            Self::Type { prefix } => format!("type for {}", prefix).into(),
+            Self::Const { is_parameter: true } => "the value of const parameter".into(),
+            Self::Const { is_parameter: false } => "the value of the constant".into(),
+        }
+    }
+}
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    /// Extracts data used by diagnostic for either types or constants
+    /// which were stuck during inference.
+    pub fn extract_inference_diagnostics_data(
+        &self,
+        arg: GenericArg<'tcx>,
+        highlight: Option<ty::print::RegionHighlightMode>,
+    ) -> InferenceDiagnosticsData {
+        match arg.unpack() {
+            GenericArgKind::Type(ty) => {
+                if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() {
+                    let mut inner = self.inner.borrow_mut();
+                    let ty_vars = &inner.type_variables();
+                    let var_origin = ty_vars.var_origin(ty_vid);
+                    if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) =
+                        var_origin.kind
+                    {
+                        if name != kw::SelfUpper {
+                            return InferenceDiagnosticsData {
+                                name: name.to_string(),
+                                span: Some(var_origin.span),
+                                kind: UnderspecifiedArgKind::Type {
+                                    prefix: "type parameter".into(),
+                                },
+                                parent: def_id.and_then(|def_id| {
+                                    InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id)
+                                }),
+                            };
+                        }
+                    }
+                }
+
+                let mut s = String::new();
+                let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
+                if let Some(highlight) = highlight {
+                    printer.region_highlight_mode = highlight;
+                }
+                let _ = ty.print(printer);
+                InferenceDiagnosticsData {
+                    name: s,
+                    span: None,
+                    kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) },
+                    parent: None,
+                }
+            }
+            GenericArgKind::Const(ct) => {
+                if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val {
+                    let origin =
+                        self.inner.borrow_mut().const_unification_table().probe_value(vid).origin;
+                    if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) =
+                        origin.kind
+                    {
+                        return InferenceDiagnosticsData {
+                            name: name.to_string(),
+                            span: Some(origin.span),
+                            kind: UnderspecifiedArgKind::Const { is_parameter: true },
+                            parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id),
+                        };
+                    }
+
+                    debug_assert!(!origin.span.is_dummy());
+                    let mut s = String::new();
+                    let mut printer =
+                        ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::ValueNS);
+                    if let Some(highlight) = highlight {
+                        printer.region_highlight_mode = highlight;
+                    }
+                    let _ = ct.print(printer);
+                    InferenceDiagnosticsData {
+                        name: s,
+                        span: Some(origin.span),
+                        kind: UnderspecifiedArgKind::Const { is_parameter: false },
+                        parent: None,
+                    }
+                } else {
+                    bug!("unexpect const: {:?}", ct);
+                }
+            }
+            GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
+        }
+    }
+
+    pub fn emit_inference_failure_err(
+        &self,
+        body_id: Option<hir::BodyId>,
+        span: Span,
+        arg: GenericArg<'tcx>,
+        impl_candidates: Vec<ty::TraitRef<'tcx>>,
+        error_code: TypeAnnotationNeeded,
+    ) -> DiagnosticBuilder<'tcx> {
+        let arg = self.resolve_vars_if_possible(arg);
+        let arg_data = self.extract_inference_diagnostics_data(arg, None);
+
+        let mut local_visitor = FindHirNodeVisitor::new(&self, arg, span);
+        let ty_to_string = |ty: Ty<'tcx>| -> String {
+            let mut s = String::new();
+            let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
+            let mut inner = self.inner.borrow_mut();
+            let ty_vars = inner.type_variables();
+            let getter = move |ty_vid| {
+                let var_origin = ty_vars.var_origin(ty_vid);
+                if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind {
+                    return Some(name.to_string());
+                }
+                None
+            };
+            printer.name_resolver = Some(Box::new(&getter));
+            let _ = if let ty::FnDef(..) = ty.kind() {
+                // We don't want the regular output for `fn`s because it includes its path in
+                // invalid pseudo-syntax, we want the `fn`-pointer output instead.
+                ty.fn_sig(self.tcx).print(printer)
+            } else {
+                ty.print(printer)
+            };
+            s
+        };
+
+        if let Some(body_id) = body_id {
+            let expr = self.tcx.hir().expect_expr(body_id.hir_id);
+            local_visitor.visit_expr(expr);
+        }
+        let err_span = if let Some(pattern) = local_visitor.found_arg_pattern {
+            pattern.span
+        } else if let Some(span) = arg_data.span {
+            // `span` here lets us point at `sum` instead of the entire right hand side expr:
+            // error[E0282]: type annotations needed
+            //  --> file2.rs:3:15
+            //   |
+            // 3 |     let _ = x.sum() as f64;
+            //   |               ^^^ cannot infer type for `S`
+            span
+        } else if let Some(ExprKind::MethodCall(_, call_span, _, _)) =
+            local_visitor.found_method_call.map(|e| &e.kind)
+        {
+            // Point at the call instead of the whole expression:
+            // error[E0284]: type annotations needed
+            //  --> file.rs:2:5
+            //   |
+            // 2 |     vec![Ok(2)].into_iter().collect()?;
+            //   |                             ^^^^^^^ cannot infer type
+            //   |
+            //   = note: cannot resolve `<_ as std::ops::Try>::Ok == _`
+            if span.contains(*call_span) { *call_span } else { span }
+        } else {
+            span
+        };
+
+        let is_named_and_not_impl_trait =
+            |ty: Ty<'_>| &ty.to_string() != "_" && !ty.is_impl_trait();
+
+        let ty_msg = match (local_visitor.found_node_ty, local_visitor.found_exact_method_call) {
+            (_, Some(_)) => String::new(),
+            (Some(ty), _) if ty.is_closure() => {
+                let substs =
+                    if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() };
+                let fn_sig = substs.as_closure().sig();
+                let args = closure_args(&fn_sig);
+                let ret = fn_sig.output().skip_binder().to_string();
+                format!(" for the closure `fn({}) -> {}`", args, ret)
+            }
+            (Some(ty), _) if is_named_and_not_impl_trait(ty) => {
+                let ty = ty_to_string(ty);
+                format!(" for `{}`", ty)
+            }
+            _ => String::new(),
+        };
+
+        // When `arg_data.name` corresponds to a type argument, show the path of the full type we're
+        // trying to infer. In the following example, `ty_msg` contains
+        // " for `std::result::Result<i32, E>`":
+        // ```
+        // error[E0282]: type annotations needed for `std::result::Result<i32, E>`
+        //  --> file.rs:L:CC
+        //   |
+        // L |     let b = Ok(4);
+        //   |         -   ^^ cannot infer type for `E` in `std::result::Result<i32, E>`
+        //   |         |
+        //   |         consider giving `b` the explicit type `std::result::Result<i32, E>`, where
+        //   |         the type parameter `E` is specified
+        // ```
+        let error_code = error_code.into();
+        let mut err = self.tcx.sess.struct_span_err_with_code(
+            err_span,
+            &format!("type annotations needed{}", ty_msg),
+            error_code,
+        );
+
+        let use_diag = local_visitor.found_use_diagnostic.as_ref();
+        if let Some(use_diag) = use_diag {
+            if use_diag.applies_to(err_span) {
+                use_diag.attach_note(&mut err);
+            }
+        }
+
+        let suffix = match local_visitor.found_node_ty {
+            Some(ty) if ty.is_closure() => {
+                let substs =
+                    if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() };
+                let fn_sig = substs.as_closure().sig();
+                let ret = fn_sig.output().skip_binder().to_string();
+
+                let closure_decl_and_body_id =
+                    local_visitor.found_closure.and_then(|closure| match &closure.kind {
+                        ExprKind::Closure(_, decl, body_id, ..) => Some((decl, *body_id)),
+                        _ => None,
+                    });
+
+                if let Some((decl, body_id)) = closure_decl_and_body_id {
+                    closure_return_type_suggestion(
+                        &mut err,
+                        &decl.output,
+                        self.tcx.hir().body(body_id),
+                        &ret,
+                    );
+                    // We don't want to give the other suggestions when the problem is the
+                    // closure return type.
+                    err.span_label(
+                        span,
+                        arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))),
+                    );
+                    return err;
+                }
+
+                // This shouldn't be reachable, but just in case we leave a reasonable fallback.
+                let args = closure_args(&fn_sig);
+                // This suggestion is incomplete, as the user will get further type inference
+                // errors due to the `_` placeholders and the introduction of `Box`, but it does
+                // nudge them in the right direction.
+                format!("a boxed closure type like `Box<dyn Fn({}) -> {}>`", args, ret)
+            }
+            Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => {
+                let ty = ty_to_string(ty);
+                format!("the explicit type `{}`, with the type parameters specified", ty)
+            }
+            Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => {
+                let ty = ty_to_string(ty);
+                format!(
+                    "the explicit type `{}`, where the type parameter `{}` is specified",
+                    ty, arg_data.name,
+                )
+            }
+            _ => "a type".to_string(),
+        };
+
+        if let Some(e) = local_visitor.found_exact_method_call {
+            if let ExprKind::MethodCall(segment, ..) = &e.kind {
+                // Suggest specifying type params or point out the return type of the call:
+                //
+                // error[E0282]: type annotations needed
+                //   --> $DIR/type-annotations-needed-expr.rs:2:39
+                //    |
+                // LL |     let _ = x.into_iter().sum() as f64;
+                //    |                           ^^^
+                //    |                           |
+                //    |                           cannot infer type for `S`
+                //    |                           help: consider specifying the type argument in
+                //    |                           the method call: `sum::<S>`
+                //    |
+                //    = note: type must be known at this point
+                //
+                // or
+                //
+                // error[E0282]: type annotations needed
+                //   --> $DIR/issue-65611.rs:59:20
+                //    |
+                // LL |     let x = buffer.last().unwrap().0.clone();
+                //    |             -------^^^^--
+                //    |             |      |
+                //    |             |      cannot infer type for `T`
+                //    |             this method call resolves to `std::option::Option<&T>`
+                //    |
+                //    = note: type must be known at this point
+                self.annotate_method_call(segment, e, &mut err);
+            }
+        } else if let Some(pattern) = local_visitor.found_arg_pattern {
+            // We don't want to show the default label for closures.
+            //
+            // So, before clearing, the output would look something like this:
+            // ```
+            // let x = |_| {  };
+            //          -  ^^^^ cannot infer type for `[_; 0]`
+            //          |
+            //          consider giving this closure parameter a type
+            // ```
+            //
+            // After clearing, it looks something like this:
+            // ```
+            // let x = |_| {  };
+            //          ^ consider giving this closure parameter the type `[_; 0]`
+            //            with the type parameter `_` specified
+            // ```
+            err.span_label(
+                pattern.span,
+                format!("consider giving this closure parameter {}", suffix),
+            );
+        } else if let Some(pattern) = local_visitor.found_local_pattern {
+            let msg = if let Some(simple_ident) = pattern.simple_ident() {
+                match pattern.span.desugaring_kind() {
+                    None => format!("consider giving `{}` {}", simple_ident, suffix),
+                    Some(DesugaringKind::ForLoop(_)) => {
+                        "the element type for this iterator is not specified".to_string()
+                    }
+                    _ => format!("this needs {}", suffix),
+                }
+            } else {
+                format!("consider giving this pattern {}", suffix)
+            };
+            err.span_label(pattern.span, msg);
+        } else if let Some(e) = local_visitor.found_method_call {
+            if let ExprKind::MethodCall(segment, _, exprs, _) = &e.kind {
+                // Suggest impl candidates:
+                //
+                // error[E0283]: type annotations needed
+                //   --> $DIR/E0283.rs:35:24
+                //    |
+                // LL |     let bar = foo_impl.into() * 1u32;
+                //    |               ---------^^^^--
+                //    |               |        |
+                //    |               |        cannot infer type for type parameter `T` declared on the trait `Into`
+                //    |               this method call resolves to `T`
+                //    |               help: specify type like: `<Impl as Into<u32>>::into(foo_impl)`
+                //    |
+                //    = note: cannot satisfy `Impl: Into<_>`
+                if !impl_candidates.is_empty() && e.span.contains(span) {
+                    if let Some(expr) = exprs.first() {
+                        if let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind {
+                            if let [path_segment] = path.segments {
+                                let candidate_len = impl_candidates.len();
+                                let suggestions = impl_candidates.iter().map(|candidate| {
+                                    format!(
+                                        "{}::{}({})",
+                                        candidate, segment.ident, path_segment.ident
+                                    )
+                                });
+                                err.span_suggestions(
+                                    e.span,
+                                    &format!(
+                                        "use the fully qualified path for the potential candidate{}",
+                                        pluralize!(candidate_len),
+                                    ),
+                                    suggestions,
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                        }
+                    };
+                }
+                // Suggest specifying type params or point out the return type of the call:
+                //
+                // error[E0282]: type annotations needed
+                //   --> $DIR/type-annotations-needed-expr.rs:2:39
+                //    |
+                // LL |     let _ = x.into_iter().sum() as f64;
+                //    |                           ^^^
+                //    |                           |
+                //    |                           cannot infer type for `S`
+                //    |                           help: consider specifying the type argument in
+                //    |                           the method call: `sum::<S>`
+                //    |
+                //    = note: type must be known at this point
+                //
+                // or
+                //
+                // error[E0282]: type annotations needed
+                //   --> $DIR/issue-65611.rs:59:20
+                //    |
+                // LL |     let x = buffer.last().unwrap().0.clone();
+                //    |             -------^^^^--
+                //    |             |      |
+                //    |             |      cannot infer type for `T`
+                //    |             this method call resolves to `std::option::Option<&T>`
+                //    |
+                //    = note: type must be known at this point
+                self.annotate_method_call(segment, e, &mut err);
+            }
+        }
+        // Instead of the following:
+        // error[E0282]: type annotations needed
+        //  --> file2.rs:3:15
+        //   |
+        // 3 |     let _ = x.sum() as f64;
+        //   |             --^^^--------- cannot infer type for `S`
+        //   |
+        //   = note: type must be known at this point
+        // We want:
+        // error[E0282]: type annotations needed
+        //  --> file2.rs:3:15
+        //   |
+        // 3 |     let _ = x.sum() as f64;
+        //   |               ^^^ cannot infer type for `S`
+        //   |
+        //   = note: type must be known at this point
+        let span = arg_data.span.unwrap_or(err_span);
+
+        // Avoid multiple labels pointing at `span`.
+        if !err
+            .span
+            .span_labels()
+            .iter()
+            .any(|span_label| span_label.label.is_some() && span_label.span == span)
+            && local_visitor.found_arg_pattern.is_none()
+        {
+            // FIXME(const_generics): we would like to handle const arguments
+            // as part of the normal diagnostics flow below, but there appear to
+            // be subtleties in doing so, so for now we special-case const args
+            // here.
+            if let (UnderspecifiedArgKind::Const { .. }, Some(parent_data)) =
+                (&arg_data.kind, &arg_data.parent)
+            {
+                // (#83606): Do not emit a suggestion if the parent has an `impl Trait`
+                // as an argument otherwise it will cause the E0282 error.
+                if !self.tcx.generics_of(parent_data.def_id).has_impl_trait()
+                    || self.tcx.features().explicit_generic_args_with_impl_trait
+                {
+                    err.span_suggestion_verbose(
+                        span,
+                        "consider specifying the const argument",
+                        format!("{}::<{}>", parent_data.name, arg_data.name),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+
+            err.span_label(
+                span,
+                arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))),
+            );
+        }
+
+        err
+    }
+
+    fn trait_def_from_hir_fn(&self, hir_id: hir::HirId) -> Option<DefId> {
+        // The DefId will be the method's trait item ID unless this is an inherent impl
+        if let Some((DefKind::AssocFn, def_id)) =
+            self.in_progress_typeck_results?.borrow().type_dependent_def(hir_id)
+        {
+            return self
+                .tcx
+                .parent(def_id)
+                .filter(|&parent_def_id| self.tcx.is_trait(parent_def_id));
+        }
+
+        None
+    }
+
+    /// If the `FnSig` for the method call can be found and type arguments are identified as
+    /// needed, suggest annotating the call, otherwise point out the resulting type of the call.
+    fn annotate_method_call(
+        &self,
+        segment: &hir::PathSegment<'_>,
+        e: &Expr<'_>,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
+        if let (Some(typeck_results), None) = (self.in_progress_typeck_results, &segment.args) {
+            let borrow = typeck_results.borrow();
+            if let Some((DefKind::AssocFn, did)) = borrow.type_dependent_def(e.hir_id) {
+                let generics = self.tcx.generics_of(did);
+                if !generics.params.is_empty() && !generics.has_impl_trait() {
+                    err.span_suggestion_verbose(
+                        segment.ident.span.shrink_to_hi(),
+                        &format!(
+                            "consider specifying the type argument{} in the method call",
+                            pluralize!(generics.params.len()),
+                        ),
+                        format!(
+                            "::<{}>",
+                            generics
+                                .params
+                                .iter()
+                                .map(|p| p.name.to_string())
+                                .collect::<Vec<String>>()
+                                .join(", ")
+                        ),
+                        Applicability::HasPlaceholders,
+                    );
+                } else {
+                    let sig = self.tcx.fn_sig(did);
+                    let bound_output = sig.output();
+                    let output = bound_output.skip_binder();
+                    err.span_label(e.span, &format!("this method call resolves to `{}`", output));
+                    let kind = output.kind();
+                    if let ty::Projection(proj) = kind {
+                        if let Some(span) = self.tcx.hir().span_if_local(proj.item_def_id) {
+                            err.span_label(span, &format!("`{}` defined here", output));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    pub fn need_type_info_err_in_generator(
+        &self,
+        kind: hir::GeneratorKind,
+        span: Span,
+        ty: Ty<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        let ty = self.resolve_vars_if_possible(ty);
+        let data = self.extract_inference_diagnostics_data(ty.into(), None);
+
+        let mut err = struct_span_err!(
+            self.tcx.sess,
+            span,
+            E0698,
+            "type inside {} must be known in this context",
+            kind,
+        );
+        err.span_label(span, data.cannot_infer_msg(None));
+        err
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
new file mode 100644
index 00000000000..1b35c4032f4
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
@@ -0,0 +1,191 @@
+//! Error Reporting for Anonymous Region Lifetime Errors
+//! where both the regions are anonymous.
+
+use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
+use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo;
+use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::lexical_region_resolve::RegionResolutionError;
+use crate::infer::SubregionOrigin;
+
+use rustc_errors::{struct_span_err, ErrorReported};
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    /// Print the error message for lifetime errors when both the concerned regions are anonymous.
+    ///
+    /// Consider a case where we have
+    ///
+    /// ```no_run
+    /// fn foo(x: &mut Vec<&u8>, y: &u8) {
+    ///     x.push(y);
+    /// }
+    /// ```
+    ///
+    /// The example gives
+    ///
+    /// ```text
+    /// fn foo(x: &mut Vec<&u8>, y: &u8) {
+    ///                    ---      --- these references are declared with different lifetimes...
+    ///     x.push(y);
+    ///     ^ ...but data from `y` flows into `x` here
+    /// ```
+    ///
+    /// It has been extended for the case of structs too.
+    ///
+    /// Consider the example
+    ///
+    /// ```no_run
+    /// struct Ref<'a> { x: &'a u32 }
+    /// ```
+    ///
+    /// ```text
+    /// fn foo(mut x: Vec<Ref>, y: Ref) {
+    ///                   ---      --- these structs are declared with different lifetimes...
+    ///     x.push(y);
+    ///     ^ ...but data from `y` flows into `x` here
+    /// }
+    /// ```
+    ///
+    /// It will later be extended to trait objects.
+    pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorReported> {
+        let (span, sub, sup) = self.regions()?;
+
+        if let Some(RegionResolutionError::ConcreteFailure(
+            SubregionOrigin::ReferenceOutlivesReferent(..),
+            ..,
+        )) = self.error
+        {
+            // This error doesn't make much sense in this case.
+            return None;
+        }
+
+        // Determine whether the sub and sup consist of both anonymous (elided) regions.
+        let anon_reg_sup = self.tcx().is_suitable_region(sup)?;
+
+        let anon_reg_sub = self.tcx().is_suitable_region(sub)?;
+        let scope_def_id_sup = anon_reg_sup.def_id;
+        let bregion_sup = anon_reg_sup.boundregion;
+        let scope_def_id_sub = anon_reg_sub.def_id;
+        let bregion_sub = anon_reg_sub.boundregion;
+
+        let ty_sup = find_anon_type(self.tcx(), sup, &bregion_sup)?;
+
+        let ty_sub = find_anon_type(self.tcx(), sub, &bregion_sub)?;
+
+        debug!(
+            "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}",
+            ty_sub, sup, bregion_sup
+        );
+        debug!(
+            "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}",
+            ty_sup, sub, bregion_sub
+        );
+
+        let (ty_sup, ty_fndecl_sup) = ty_sup;
+        let (ty_sub, ty_fndecl_sub) = ty_sub;
+
+        let AnonymousParamInfo { param: anon_param_sup, .. } =
+            self.find_param_with_region(sup, sup)?;
+        let AnonymousParamInfo { param: anon_param_sub, .. } =
+            self.find_param_with_region(sub, sub)?;
+
+        let sup_is_ret_type =
+            self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup);
+        let sub_is_ret_type =
+            self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
+
+        let span_label_var1 = match anon_param_sup.pat.simple_ident() {
+            Some(simple_ident) => format!(" from `{}`", simple_ident),
+            None => String::new(),
+        };
+
+        let span_label_var2 = match anon_param_sub.pat.simple_ident() {
+            Some(simple_ident) => format!(" into `{}`", simple_ident),
+            None => String::new(),
+        };
+
+        let (span_1, span_2, main_label, span_label, future_return_type) =
+            match (sup_is_ret_type, sub_is_ret_type) {
+                (None, None) => {
+                    let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id {
+                        (
+                            "this type is declared with multiple lifetimes...".to_owned(),
+                            "...but data with one lifetime flows into the other here".to_owned(),
+                        )
+                    } else {
+                        (
+                            "these two types are declared with different lifetimes...".to_owned(),
+                            format!("...but data{} flows{} here", span_label_var1, span_label_var2),
+                        )
+                    };
+                    (ty_sup.span, ty_sub.span, main_label_1, span_label_1, None)
+                }
+
+                (Some(ret_span), _) => {
+                    let sup_future = self.future_return_type(scope_def_id_sup);
+                    let (return_type, action) = if sup_future.is_some() {
+                        ("returned future", "held across an await point")
+                    } else {
+                        ("return type", "returned")
+                    };
+
+                    (
+                        ty_sub.span,
+                        ret_span,
+                        format!(
+                            "this parameter and the {} are declared with different lifetimes...",
+                            return_type
+                        ),
+                        format!("...but data{} is {} here", span_label_var1, action),
+                        sup_future,
+                    )
+                }
+                (_, Some(ret_span)) => {
+                    let sub_future = self.future_return_type(scope_def_id_sub);
+                    let (return_type, action) = if sub_future.is_some() {
+                        ("returned future", "held across an await point")
+                    } else {
+                        ("return type", "returned")
+                    };
+
+                    (
+                        ty_sup.span,
+                        ret_span,
+                        format!(
+                            "this parameter and the {} are declared with different lifetimes...",
+                            return_type
+                        ),
+                        format!("...but data{} is {} here", span_label_var1, action),
+                        sub_future,
+                    )
+                }
+            };
+
+        let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch");
+
+        e.span_label(span_1, main_label);
+        e.span_label(span_2, String::new());
+        e.span_label(span, span_label);
+
+        if let Some(t) = future_return_type {
+            let snip = self
+                .tcx()
+                .sess
+                .source_map()
+                .span_to_snippet(t.span)
+                .ok()
+                .and_then(|s| match (&t.kind, s.as_str()) {
+                    (rustc_hir::TyKind::Tup(&[]), "") => Some("()".to_string()),
+                    (_, "") => None,
+                    _ => Some(s),
+                })
+                .unwrap_or("{unnamed_type}".to_string());
+
+            e.span_label(
+                t.span,
+                &format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip),
+            );
+        }
+        e.emit();
+        Some(ErrorReported)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
new file mode 100644
index 00000000000..58eb1e9aa12
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
@@ -0,0 +1,274 @@
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::Node;
+use rustc_middle::hir::map::Map;
+use rustc_middle::middle::resolve_lifetime as rl;
+use rustc_middle::ty::{self, Region, TyCtxt};
+
+/// This function calls the `visit_ty` method for the parameters
+/// corresponding to the anonymous regions. The `nested_visitor.found_type`
+/// contains the anonymous type.
+///
+/// # Arguments
+/// region - the anonymous region corresponding to the anon_anon conflict
+/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
+///
+/// # Example
+/// ```
+/// fn foo(x: &mut Vec<&u8>, y: &u8)
+///    { x.push(y); }
+/// ```
+/// The function returns the nested type corresponding to the anonymous region
+/// for e.g., `&u8` and `Vec<&u8>`.
+pub(crate) fn find_anon_type(
+    tcx: TyCtxt<'tcx>,
+    region: Region<'tcx>,
+    br: &ty::BoundRegionKind,
+) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnDecl<'tcx>)> {
+    if let Some(anon_reg) = tcx.is_suitable_region(region) {
+        let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
+        let fndecl = match tcx.hir().get(hir_id) {
+            Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref m, ..), .. })
+            | Node::TraitItem(&hir::TraitItem {
+                kind: hir::TraitItemKind::Fn(ref m, ..), ..
+            })
+            | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref m, ..), .. }) => {
+                &m.decl
+            }
+            _ => return None,
+        };
+
+        fndecl
+            .inputs
+            .iter()
+            .find_map(|arg| find_component_for_bound_region(tcx, arg, br))
+            .map(|ty| (ty, &**fndecl))
+    } else {
+        None
+    }
+}
+
+// This method creates a FindNestedTypeVisitor which returns the type corresponding
+// to the anonymous region.
+fn find_component_for_bound_region(
+    tcx: TyCtxt<'tcx>,
+    arg: &'tcx hir::Ty<'tcx>,
+    br: &ty::BoundRegionKind,
+) -> Option<&'tcx hir::Ty<'tcx>> {
+    let mut nested_visitor = FindNestedTypeVisitor {
+        tcx,
+        bound_region: *br,
+        found_type: None,
+        current_index: ty::INNERMOST,
+    };
+    nested_visitor.visit_ty(arg);
+    nested_visitor.found_type
+}
+
+// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
+// anonymous region. The example above would lead to a conflict between
+// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
+// would be invoked twice, once for each lifetime, and would
+// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
+// where that lifetime appears. This allows us to highlight the
+// specific part of the type in the error message.
+struct FindNestedTypeVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    // The bound_region corresponding to the Refree(freeregion)
+    // associated with the anonymous region we are looking for.
+    bound_region: ty::BoundRegionKind,
+    // The type where the anonymous lifetime appears
+    // for e.g., Vec<`&u8`> and <`&u8`>
+    found_type: Option<&'tcx hir::Ty<'tcx>>,
+    current_index: ty::DebruijnIndex,
+}
+
+impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.tcx.hir())
+    }
+
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
+        match arg.kind {
+            hir::TyKind::BareFn(_) => {
+                self.current_index.shift_in(1);
+                intravisit::walk_ty(self, arg);
+                self.current_index.shift_out(1);
+                return;
+            }
+
+            hir::TyKind::TraitObject(bounds, ..) => {
+                for bound in bounds {
+                    self.current_index.shift_in(1);
+                    self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
+                    self.current_index.shift_out(1);
+                }
+            }
+
+            hir::TyKind::Rptr(ref lifetime, _) => {
+                // the lifetime of the TyRptr
+                let hir_id = lifetime.hir_id;
+                match (self.tcx.named_region(hir_id), self.bound_region) {
+                    // Find the index of the anonymous region that was part of the
+                    // error. We will then search the function parameters for a bound
+                    // region at the right depth with the same index
+                    (
+                        Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)),
+                        ty::BrAnon(br_index),
+                    ) => {
+                        debug!(
+                            "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}",
+                            debruijn_index, anon_index, br_index
+                        );
+                        if debruijn_index == self.current_index && anon_index == br_index {
+                            self.found_type = Some(arg);
+                            return; // we can stop visiting now
+                        }
+                    }
+
+                    // Find the index of the named region that was part of the
+                    // error. We will then search the function parameters for a bound
+                    // region at the right depth with the same index
+                    (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => {
+                        debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
+                        if id == def_id {
+                            self.found_type = Some(arg);
+                            return; // we can stop visiting now
+                        }
+                    }
+
+                    // Find the index of the named region that was part of the
+                    // error. We will then search the function parameters for a bound
+                    // region at the right depth with the same index
+                    (
+                        Some(rl::Region::LateBound(debruijn_index, _, id, _)),
+                        ty::BrNamed(def_id, _),
+                    ) => {
+                        debug!(
+                            "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
+                            debruijn_index
+                        );
+                        debug!("LateBound id={:?} def_id={:?}", id, def_id);
+                        if debruijn_index == self.current_index && id == def_id {
+                            self.found_type = Some(arg);
+                            return; // we can stop visiting now
+                        }
+                    }
+
+                    (
+                        Some(
+                            rl::Region::Static
+                            | rl::Region::Free(_, _)
+                            | rl::Region::EarlyBound(_, _, _)
+                            | rl::Region::LateBound(_, _, _, _)
+                            | rl::Region::LateBoundAnon(_, _, _),
+                        )
+                        | None,
+                        _,
+                    ) => {
+                        debug!("no arg found");
+                    }
+                }
+            }
+            // Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
+            hir::TyKind::Path(_) => {
+                let subvisitor = &mut TyPathVisitor {
+                    tcx: self.tcx,
+                    found_it: false,
+                    bound_region: self.bound_region,
+                    current_index: self.current_index,
+                };
+                intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
+                // this will visit only outermost type
+                if subvisitor.found_it {
+                    self.found_type = Some(arg);
+                }
+            }
+            _ => {}
+        }
+        // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
+        // go on to visit `&Foo`
+        intravisit::walk_ty(self, arg);
+    }
+}
+
+// The visitor captures the corresponding `hir::Ty` of the anonymous region
+// in the case of structs ie. `hir::TyKind::Path`.
+// This visitor would be invoked for each lifetime corresponding to a struct,
+// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
+// where that lifetime appears. This allows us to highlight the
+// specific part of the type in the error message.
+struct TyPathVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    found_it: bool,
+    bound_region: ty::BoundRegionKind,
+    current_index: ty::DebruijnIndex,
+}
+
+impl Visitor<'tcx> for TyPathVisitor<'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Map<'tcx>> {
+        NestedVisitorMap::OnlyBodies(self.tcx.hir())
+    }
+
+    fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
+        match (self.tcx.named_region(lifetime.hir_id), self.bound_region) {
+            // the lifetime of the TyPath!
+            (
+                Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)),
+                ty::BrAnon(br_index),
+            ) => {
+                if debruijn_index == self.current_index && anon_index == br_index {
+                    self.found_it = true;
+                    return;
+                }
+            }
+
+            (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => {
+                debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
+                if id == def_id {
+                    self.found_it = true;
+                    return; // we can stop visiting now
+                }
+            }
+
+            (Some(rl::Region::LateBound(debruijn_index, _, id, _)), ty::BrNamed(def_id, _)) => {
+                debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,);
+                debug!("id={:?}", id);
+                debug!("def_id={:?}", def_id);
+                if debruijn_index == self.current_index && id == def_id {
+                    self.found_it = true;
+                    return; // we can stop visiting now
+                }
+            }
+
+            (
+                Some(
+                    rl::Region::Static
+                    | rl::Region::EarlyBound(_, _, _)
+                    | rl::Region::LateBound(_, _, _, _)
+                    | rl::Region::LateBoundAnon(_, _, _)
+                    | rl::Region::Free(_, _),
+                )
+                | None,
+                _,
+            ) => {
+                debug!("no arg found");
+            }
+        }
+    }
+
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
+        // ignore nested types
+        //
+        // If you have a type like `Foo<'a, &Ty>` we
+        // are only interested in the immediate lifetimes ('a).
+        //
+        // Making `visit_ty` empty will ignore the `&Ty` embedded
+        // inside, it will get reached by the outer visitor.
+        debug!("`Ty` corresponding to a struct is {:?}", arg);
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
new file mode 100644
index 00000000000..c60a7149e40
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
@@ -0,0 +1,103 @@
+//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
+//! to hold.
+
+use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::error_reporting::note_and_explain_region;
+use crate::infer::lexical_region_resolve::RegionResolutionError;
+use crate::infer::{SubregionOrigin, TypeTrace};
+use crate::traits::ObligationCauseCode;
+use rustc_data_structures::stable_set::FxHashSet;
+use rustc_errors::{Applicability, ErrorReported};
+use rustc_hir as hir;
+use rustc_hir::intravisit::Visitor;
+use rustc_middle::ty::{self, TypeVisitor};
+use rustc_span::MultiSpan;
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorReported> {
+        let error = self.error.as_ref()?;
+        debug!("try_report_mismatched_static_lifetime {:?}", error);
+
+        let (origin, sub, sup) = match error.clone() {
+            RegionResolutionError::ConcreteFailure(origin, sub, sup) => (origin, sub, sup),
+            _ => return None,
+        };
+        if *sub != ty::RegionKind::ReStatic {
+            return None;
+        }
+        let cause = match origin {
+            SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) => cause,
+            _ => return None,
+        };
+        let (parent, impl_def_id) = match &cause.code {
+            ObligationCauseCode::MatchImpl(parent, impl_def_id) => (parent, impl_def_id),
+            _ => return None,
+        };
+        let binding_span = match **parent {
+            ObligationCauseCode::BindingObligation(_def_id, binding_span) => binding_span,
+            _ => return None,
+        };
+        let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type");
+        // FIXME: we should point at the lifetime
+        let mut multi_span: MultiSpan = vec![binding_span].into();
+        multi_span
+            .push_span_label(binding_span, "introduces a `'static` lifetime requirement".into());
+        err.span_note(multi_span, "because this has an unmet lifetime requirement");
+        note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span));
+        if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
+            // If an impl is local, then maybe this isn't what they want. Try to
+            // be as helpful as possible with implicit lifetimes.
+
+            // First, let's get the hir self type of the impl
+            let impl_self_ty = match impl_node {
+                hir::Node::Item(hir::Item {
+                    kind: hir::ItemKind::Impl(hir::Impl { self_ty, .. }),
+                    ..
+                }) => self_ty,
+                _ => bug!("Node not an impl."),
+            };
+
+            // Next, let's figure out the set of trait objects with implict static bounds
+            let ty = self.tcx().type_of(*impl_def_id);
+            let mut v = super::static_impl_trait::TraitObjectVisitor(FxHashSet::default());
+            v.visit_ty(ty);
+            let mut traits = vec![];
+            for matching_def_id in v.0 {
+                let mut hir_v =
+                    super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id);
+                hir_v.visit_ty(&impl_self_ty);
+            }
+
+            if traits.is_empty() {
+                // If there are no trait object traits to point at, either because
+                // there aren't trait objects or because none are implicit, then just
+                // write a single note on the impl itself.
+
+                let impl_span = self.tcx().def_span(*impl_def_id);
+                err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`");
+            } else {
+                // Otherwise, point at all implicit static lifetimes
+
+                err.note("...does not necessarily outlive the static lifetime introduced by the compatible `impl`");
+                for span in &traits {
+                    err.span_note(*span, "this has an implicit `'static` lifetime requirement");
+                    // It would be nice to put this immediately under the above note, but they get
+                    // pushed to the end.
+                    err.span_suggestion_verbose(
+                        span.shrink_to_hi(),
+                        "consider relaxing the implicit `'static` requirement",
+                        " + '_".to_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        } else {
+            // Otherwise just point out the impl.
+
+            let impl_span = self.tcx().def_span(*impl_def_id);
+            err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`");
+        }
+        err.emit();
+        Some(ErrorReported)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
new file mode 100644
index 00000000000..3f27bf67b59
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
@@ -0,0 +1,75 @@
+use crate::infer::lexical_region_resolve::RegionResolutionError;
+use crate::infer::lexical_region_resolve::RegionResolutionError::*;
+use crate::infer::InferCtxt;
+use rustc_errors::{DiagnosticBuilder, ErrorReported};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::source_map::Span;
+
+mod different_lifetimes;
+pub mod find_anon_type;
+mod mismatched_static_lifetime;
+mod named_anon_conflict;
+mod placeholder_error;
+mod static_impl_trait;
+mod trait_impl_difference;
+mod util;
+
+impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
+    pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
+        NiceRegionError::new(self, error.clone()).try_report().is_some()
+    }
+}
+
+pub struct NiceRegionError<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'cx, 'tcx>,
+    error: Option<RegionResolutionError<'tcx>>,
+    regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>,
+}
+
+impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
+    pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>, error: RegionResolutionError<'tcx>) -> Self {
+        Self { infcx, error: Some(error), regions: None }
+    }
+
+    pub fn new_from_span(
+        infcx: &'cx InferCtxt<'cx, 'tcx>,
+        span: Span,
+        sub: ty::Region<'tcx>,
+        sup: ty::Region<'tcx>,
+    ) -> Self {
+        Self { infcx, error: None, regions: Some((span, sub, sup)) }
+    }
+
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    pub fn try_report_from_nll(&self) -> Option<DiagnosticBuilder<'tcx>> {
+        // Due to the improved diagnostics returned by the MIR borrow checker, only a subset of
+        // the nice region errors are required when running under the MIR borrow checker.
+        self.try_report_named_anon_conflict().or_else(|| self.try_report_placeholder_conflict())
+    }
+
+    pub fn try_report(&self) -> Option<ErrorReported> {
+        self.try_report_from_nll()
+            .map(|mut diag| {
+                diag.emit();
+                ErrorReported
+            })
+            .or_else(|| self.try_report_impl_not_conforming_to_trait())
+            .or_else(|| self.try_report_anon_anon_conflict())
+            .or_else(|| self.try_report_static_impl_trait())
+            .or_else(|| self.try_report_mismatched_static_lifetime())
+    }
+
+    pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
+        match (&self.error, self.regions) {
+            (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), sub, sup)),
+            (Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => {
+                Some((origin.span(), sub, sup))
+            }
+            (None, Some((span, sub, sup))) => Some((span, sub, sup)),
+            _ => None,
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
new file mode 100644
index 00000000000..0878f8550da
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -0,0 +1,130 @@
+//! Error Reporting for Anonymous Region Lifetime Errors
+//! where one region is named and the other is anonymous.
+use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
+use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::FnRetTy;
+use rustc_middle::ty;
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    /// When given a `ConcreteFailure` for a function with parameters containing a named region and
+    /// an anonymous region, emit an descriptive diagnostic error.
+    pub(super) fn try_report_named_anon_conflict(&self) -> Option<DiagnosticBuilder<'tcx>> {
+        let (span, sub, sup) = self.regions()?;
+
+        debug!(
+            "try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
+            sub, sup, self.error,
+        );
+
+        // Determine whether the sub and sup consist of one named region ('a)
+        // and one anonymous (elided) region. If so, find the parameter arg
+        // where the anonymous region appears (there must always be one; we
+        // only introduced anonymous regions in parameters) as well as a
+        // version new_ty of its type where the anonymous region is replaced
+        // with the named one.
+        let (named, anon, anon_param_info, region_info) = if sub.has_name()
+            && self.tcx().is_suitable_region(sup).is_some()
+            && self.find_param_with_region(sup, sub).is_some()
+        {
+            (
+                sub,
+                sup,
+                self.find_param_with_region(sup, sub).unwrap(),
+                self.tcx().is_suitable_region(sup).unwrap(),
+            )
+        } else if sup.has_name()
+            && self.tcx().is_suitable_region(sub).is_some()
+            && self.find_param_with_region(sub, sup).is_some()
+        {
+            (
+                sup,
+                sub,
+                self.find_param_with_region(sub, sup).unwrap(),
+                self.tcx().is_suitable_region(sub).unwrap(),
+            )
+        } else {
+            return None; // inapplicable
+        };
+
+        debug!("try_report_named_anon_conflict: named = {:?}", named);
+        debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info);
+        debug!("try_report_named_anon_conflict: region_info = {:?}", region_info);
+
+        let (param, new_ty, new_ty_span, br, is_first, scope_def_id, is_impl_item) = (
+            anon_param_info.param,
+            anon_param_info.param_ty,
+            anon_param_info.param_ty_span,
+            anon_param_info.bound_region,
+            anon_param_info.is_first,
+            region_info.def_id,
+            region_info.is_impl_item,
+        );
+        match br {
+            ty::BrAnon(_) => {}
+            _ => {
+                /* not an anonymous region */
+                debug!("try_report_named_anon_conflict: not an anonymous region");
+                return None;
+            }
+        }
+
+        if is_impl_item {
+            debug!("try_report_named_anon_conflict: impl item, bail out");
+            return None;
+        }
+
+        if let Some((_, fndecl)) = find_anon_type(self.tcx(), anon, &br) {
+            if self.is_self_anon(is_first, scope_def_id) {
+                return None;
+            }
+
+            if let FnRetTy::Return(ty) = &fndecl.output {
+                let mut v = ty::TraitObjectVisitor(vec![], self.tcx().hir());
+                v.visit_ty(ty);
+
+                debug!("try_report_named_anon_conflict: ret ty {:?}", ty);
+                if sub == &ty::ReStatic
+                    && v.0.into_iter().any(|t| t.span.desugaring_kind().is_none())
+                {
+                    // If the failure is due to a `'static` requirement coming from a `dyn` or
+                    // `impl` Trait that *isn't* caused by `async fn` desugaring, handle this case
+                    // better in `static_impl_trait`.
+                    debug!("try_report_named_anon_conflict: impl Trait + 'static");
+                    return None;
+                }
+            }
+        }
+
+        let (error_var, span_label_var) = match param.pat.simple_ident() {
+            Some(simple_ident) => (
+                format!("the type of `{}`", simple_ident),
+                format!("the type of `{}`", simple_ident),
+            ),
+            None => ("parameter type".to_owned(), "type".to_owned()),
+        };
+
+        let mut diag = struct_span_err!(
+            self.tcx().sess,
+            span,
+            E0621,
+            "explicit lifetime required in {}",
+            error_var
+        );
+
+        diag.span_label(span, format!("lifetime `{}` required", named));
+        // Suggesting `'static` is nearly always incorrect, and can steer users
+        // down the wrong path.
+        if *named != ty::ReStatic {
+            diag.span_suggestion(
+                new_ty_span,
+                &format!("add explicit lifetime `{}` to {}", named, span_label_var),
+                new_ty.to_string(),
+                Applicability::Unspecified,
+            );
+        }
+
+        Some(diag)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
new file mode 100644
index 00000000000..4aecc2f40b8
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -0,0 +1,493 @@
+use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::lexical_region_resolve::RegionResolutionError;
+use crate::infer::ValuePairs;
+use crate::infer::{SubregionOrigin, TypeTrace};
+use crate::traits::{ObligationCause, ObligationCauseCode};
+use rustc_errors::DiagnosticBuilder;
+use rustc_hir::def::Namespace;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::error::ExpectedFound;
+use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode};
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{self, TyCtxt};
+
+use std::fmt::{self, Write};
+
+impl NiceRegionError<'me, 'tcx> {
+    /// When given a `ConcreteFailure` for a function with arguments containing a named region and
+    /// an anonymous region, emit a descriptive diagnostic error.
+    pub(super) fn try_report_placeholder_conflict(&self) -> Option<DiagnosticBuilder<'tcx>> {
+        match &self.error {
+            ///////////////////////////////////////////////////////////////////////////
+            // NB. The ordering of cases in this match is very
+            // sensitive, because we are often matching against
+            // specific cases and then using an `_` to match all
+            // others.
+
+            ///////////////////////////////////////////////////////////////////////////
+            // Check for errors from comparing trait failures -- first
+            // with two placeholders, then with one.
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_placeholder @ ty::RePlaceholder(_),
+                _,
+                sup_placeholder @ ty::RePlaceholder(_),
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(self.tcx().mk_region(ty::ReVar(*vid))),
+                cause,
+                Some(sub_placeholder),
+                Some(sup_placeholder),
+                values,
+            ),
+
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_placeholder @ ty::RePlaceholder(_),
+                _,
+                _,
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(self.tcx().mk_region(ty::ReVar(*vid))),
+                cause,
+                Some(sub_placeholder),
+                None,
+                values,
+            ),
+
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                _,
+                _,
+                sup_placeholder @ ty::RePlaceholder(_),
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(self.tcx().mk_region(ty::ReVar(*vid))),
+                cause,
+                None,
+                Some(*sup_placeholder),
+                values,
+            ),
+
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                _,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sup_placeholder @ ty::RePlaceholder(_),
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(self.tcx().mk_region(ty::ReVar(*vid))),
+                cause,
+                None,
+                Some(*sup_placeholder),
+                values,
+            ),
+
+            Some(RegionResolutionError::UpperBoundUniverseConflict(
+                vid,
+                _,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sup_placeholder @ ty::RePlaceholder(_),
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(self.tcx().mk_region(ty::ReVar(*vid))),
+                cause,
+                None,
+                Some(*sup_placeholder),
+                values,
+            ),
+
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_region @ ty::RePlaceholder(_),
+                sup_region @ ty::RePlaceholder(_),
+            )) => self.try_report_trait_placeholder_mismatch(
+                None,
+                cause,
+                Some(*sub_region),
+                Some(*sup_region),
+                values,
+            ),
+
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_region @ ty::RePlaceholder(_),
+                sup_region,
+            )) => self.try_report_trait_placeholder_mismatch(
+                (!sup_region.has_name()).then_some(sup_region),
+                cause,
+                Some(sub_region),
+                None,
+                values,
+            ),
+
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_region,
+                sup_region @ ty::RePlaceholder(_),
+            )) => self.try_report_trait_placeholder_mismatch(
+                (!sub_region.has_name()).then_some(sub_region),
+                cause,
+                None,
+                Some(sup_region),
+                values,
+            ),
+
+            _ => None,
+        }
+    }
+
+    fn try_report_trait_placeholder_mismatch(
+        &self,
+        vid: Option<ty::Region<'tcx>>,
+        cause: &ObligationCause<'tcx>,
+        sub_placeholder: Option<ty::Region<'tcx>>,
+        sup_placeholder: Option<ty::Region<'tcx>>,
+        value_pairs: &ValuePairs<'tcx>,
+    ) -> Option<DiagnosticBuilder<'tcx>> {
+        let (expected_substs, found_substs, trait_def_id) = match value_pairs {
+            ValuePairs::TraitRefs(ExpectedFound { expected, found })
+                if expected.def_id == found.def_id =>
+            {
+                (expected.substs, found.substs, expected.def_id)
+            }
+            ValuePairs::PolyTraitRefs(ExpectedFound { expected, found })
+                if expected.def_id() == found.def_id() =>
+            {
+                // It's possible that the placeholders come from a binder
+                // outside of this value pair. Use `no_bound_vars` as a
+                // simple heuristic for that.
+                (expected.no_bound_vars()?.substs, found.no_bound_vars()?.substs, expected.def_id())
+            }
+            _ => return None,
+        };
+
+        Some(self.report_trait_placeholder_mismatch(
+            vid,
+            cause,
+            sub_placeholder,
+            sup_placeholder,
+            trait_def_id,
+            expected_substs,
+            found_substs,
+        ))
+    }
+
+    // error[E0308]: implementation of `Foo` does not apply to enough lifetimes
+    //   --> /home/nmatsakis/tmp/foo.rs:12:5
+    //    |
+    // 12 |     all::<&'static u32>();
+    //    |     ^^^^^^^^^^^^^^^^^^^ lifetime mismatch
+    //    |
+    //    = note: Due to a where-clause on the function `all`,
+    //    = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.
+    //    = note: However, the type `T` only implements `...` for some specific lifetime `'2`.
+    #[instrument(level = "debug", skip(self))]
+    fn report_trait_placeholder_mismatch(
+        &self,
+        vid: Option<ty::Region<'tcx>>,
+        cause: &ObligationCause<'tcx>,
+        sub_placeholder: Option<ty::Region<'tcx>>,
+        sup_placeholder: Option<ty::Region<'tcx>>,
+        trait_def_id: DefId,
+        expected_substs: SubstsRef<'tcx>,
+        actual_substs: SubstsRef<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        let span = cause.span(self.tcx());
+        let msg = format!(
+            "implementation of `{}` is not general enough",
+            self.tcx().def_path_str(trait_def_id),
+        );
+        let mut err = self.tcx().sess.struct_span_err(span, &msg);
+
+        let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) = cause.code {
+            err.span_label(span, "doesn't satisfy where-clause");
+            err.span_label(
+                self.tcx().def_span(def_id),
+                &format!("due to a where-clause on `{}`...", self.tcx().def_path_str(def_id)),
+            );
+            true
+        } else {
+            err.span_label(span, &msg);
+            false
+        };
+
+        let expected_trait_ref = self.infcx.resolve_vars_if_possible(ty::TraitRef {
+            def_id: trait_def_id,
+            substs: expected_substs,
+        });
+        let actual_trait_ref = self
+            .infcx
+            .resolve_vars_if_possible(ty::TraitRef { def_id: trait_def_id, substs: actual_substs });
+
+        // Search the expected and actual trait references to see (a)
+        // whether the sub/sup placeholders appear in them (sometimes
+        // you have a trait ref like `T: Foo<fn(&u8)>`, where the
+        // placeholder was created as part of an inner type) and (b)
+        // whether the inference variable appears. In each case,
+        // assign a counter value in each case if so.
+        let mut counter = 0;
+        let mut has_sub = None;
+        let mut has_sup = None;
+
+        let mut actual_has_vid = None;
+        let mut expected_has_vid = None;
+
+        self.tcx().for_each_free_region(&expected_trait_ref, |r| {
+            if Some(r) == sub_placeholder && has_sub.is_none() {
+                has_sub = Some(counter);
+                counter += 1;
+            } else if Some(r) == sup_placeholder && has_sup.is_none() {
+                has_sup = Some(counter);
+                counter += 1;
+            }
+
+            if Some(r) == vid && expected_has_vid.is_none() {
+                expected_has_vid = Some(counter);
+                counter += 1;
+            }
+        });
+
+        self.tcx().for_each_free_region(&actual_trait_ref, |r| {
+            if Some(r) == vid && actual_has_vid.is_none() {
+                actual_has_vid = Some(counter);
+                counter += 1;
+            }
+        });
+
+        let actual_self_ty_has_vid =
+            self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
+
+        let expected_self_ty_has_vid =
+            self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);
+
+        let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;
+
+        debug!(
+            ?actual_has_vid,
+            ?expected_has_vid,
+            ?has_sub,
+            ?has_sup,
+            ?actual_self_ty_has_vid,
+            ?expected_self_ty_has_vid,
+        );
+
+        self.explain_actual_impl_that_was_found(
+            &mut err,
+            sub_placeholder,
+            sup_placeholder,
+            has_sub,
+            has_sup,
+            expected_trait_ref,
+            actual_trait_ref,
+            vid,
+            expected_has_vid,
+            actual_has_vid,
+            any_self_ty_has_vid,
+            leading_ellipsis,
+        );
+
+        err
+    }
+
+    /// Add notes with details about the expected and actual trait refs, with attention to cases
+    /// when placeholder regions are involved: either the trait or the self type containing
+    /// them needs to be mentioned the closest to the placeholders.
+    /// This makes the error messages read better, however at the cost of some complexity
+    /// due to the number of combinations we have to deal with.
+    fn explain_actual_impl_that_was_found(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        sub_placeholder: Option<ty::Region<'tcx>>,
+        sup_placeholder: Option<ty::Region<'tcx>>,
+        has_sub: Option<usize>,
+        has_sup: Option<usize>,
+        expected_trait_ref: ty::TraitRef<'tcx>,
+        actual_trait_ref: ty::TraitRef<'tcx>,
+        vid: Option<ty::Region<'tcx>>,
+        expected_has_vid: Option<usize>,
+        actual_has_vid: Option<usize>,
+        any_self_ty_has_vid: bool,
+        leading_ellipsis: bool,
+    ) {
+        // HACK(eddyb) maybe move this in a more central location.
+        #[derive(Copy, Clone)]
+        struct Highlighted<'tcx, T> {
+            tcx: TyCtxt<'tcx>,
+            highlight: RegionHighlightMode,
+            value: T,
+        }
+
+        impl<'tcx, T> Highlighted<'tcx, T> {
+            fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
+                Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) }
+            }
+        }
+
+        impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
+        where
+            T: for<'a, 'b, 'c> Print<
+                'tcx,
+                FmtPrinter<'a, 'tcx, &'b mut fmt::Formatter<'c>>,
+                Error = fmt::Error,
+            >,
+        {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                let mut printer = ty::print::FmtPrinter::new(self.tcx, f, Namespace::TypeNS);
+                printer.region_highlight_mode = self.highlight;
+
+                self.value.print(printer)?;
+                Ok(())
+            }
+        }
+
+        // The weird thing here with the `maybe_highlighting_region` calls and the
+        // the match inside is meant to be like this:
+        //
+        // - The match checks whether the given things (placeholders, etc) appear
+        //   in the types are about to print
+        // - Meanwhile, the `maybe_highlighting_region` calls set up
+        //   highlights so that, if they do appear, we will replace
+        //   them `'0` and whatever.  (This replacement takes place
+        //   inside the closure given to `maybe_highlighting_region`.)
+        //
+        // There is some duplication between the calls -- i.e., the
+        // `maybe_highlighting_region` checks if (e.g.) `has_sub` is
+        // None, an then we check again inside the closure, but this
+        // setup sort of minimized the number of calls and so form.
+
+        let highlight_trait_ref = |trait_ref| Highlighted {
+            tcx: self.tcx(),
+            highlight: RegionHighlightMode::default(),
+            value: trait_ref,
+        };
+
+        let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
+
+        let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
+        expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
+        expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
+        err.note(&{
+            let passive_voice = match (has_sub, has_sup) {
+                (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
+                (None, None) => {
+                    expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
+                    match expected_has_vid {
+                        Some(_) => true,
+                        None => any_self_ty_has_vid,
+                    }
+                }
+            };
+
+            let mut note = if same_self_type {
+                let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
+                self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
+
+                if self_ty.value.is_closure()
+                    && self
+                        .tcx()
+                        .fn_trait_kind_from_lang_item(expected_trait_ref.value.def_id)
+                        .is_some()
+                {
+                    let closure_sig = self_ty.map(|closure| {
+                        if let ty::Closure(_, substs) = closure.kind() {
+                            self.tcx().signature_unclosure(
+                                substs.as_closure().sig(),
+                                rustc_hir::Unsafety::Normal,
+                            )
+                        } else {
+                            bug!("type is not longer closure");
+                        }
+                    });
+
+                    format!(
+                        "{}closure with signature `{}` must implement `{}`",
+                        if leading_ellipsis { "..." } else { "" },
+                        closure_sig,
+                        expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+                    )
+                } else {
+                    format!(
+                        "{}`{}` must implement `{}`",
+                        if leading_ellipsis { "..." } else { "" },
+                        self_ty,
+                        expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+                    )
+                }
+            } else if passive_voice {
+                format!(
+                    "{}`{}` would have to be implemented for the type `{}`",
+                    if leading_ellipsis { "..." } else { "" },
+                    expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+                    expected_trait_ref.map(|tr| tr.self_ty()),
+                )
+            } else {
+                format!(
+                    "{}`{}` must implement `{}`",
+                    if leading_ellipsis { "..." } else { "" },
+                    expected_trait_ref.map(|tr| tr.self_ty()),
+                    expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+                )
+            };
+
+            match (has_sub, has_sup) {
+                (Some(n1), Some(n2)) => {
+                    let _ = write!(
+                        note,
+                        ", for any two lifetimes `'{}` and `'{}`...",
+                        std::cmp::min(n1, n2),
+                        std::cmp::max(n1, n2),
+                    );
+                }
+                (Some(n), _) | (_, Some(n)) => {
+                    let _ = write!(note, ", for any lifetime `'{}`...", n,);
+                }
+                (None, None) => {
+                    if let Some(n) = expected_has_vid {
+                        let _ = write!(note, ", for some specific lifetime `'{}`...", n,);
+                    }
+                }
+            }
+
+            note
+        });
+
+        let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
+        actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
+        err.note(&{
+            let passive_voice = match actual_has_vid {
+                Some(_) => any_self_ty_has_vid,
+                None => true,
+            };
+
+            let mut note = if same_self_type {
+                format!(
+                    "...but it actually implements `{}`",
+                    actual_trait_ref.map(|tr| tr.print_only_trait_path()),
+                )
+            } else if passive_voice {
+                format!(
+                    "...but `{}` is actually implemented for the type `{}`",
+                    actual_trait_ref.map(|tr| tr.print_only_trait_path()),
+                    actual_trait_ref.map(|tr| tr.self_ty()),
+                )
+            } else {
+                format!(
+                    "...but `{}` actually implements `{}`",
+                    actual_trait_ref.map(|tr| tr.self_ty()),
+                    actual_trait_ref.map(|tr| tr.print_only_trait_path()),
+                )
+            };
+
+            if let Some(n) = actual_has_vid {
+                let _ = write!(note, ", for some specific lifetime `'{}`", n);
+            }
+
+            note
+        });
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
new file mode 100644
index 00000000000..fde4ec05ffc
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -0,0 +1,518 @@
+//! Error Reporting for static impl Traits.
+
+use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::lexical_region_resolve::RegionResolutionError;
+use crate::infer::{SubregionOrigin, TypeTrace};
+use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
+use rustc_data_structures::stable_set::FxHashSet;
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
+use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, TypeVisitor};
+use rustc_span::symbol::Ident;
+use rustc_span::{MultiSpan, Span};
+
+use std::ops::ControlFlow;
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    /// Print the error message for lifetime errors when the return type is a static `impl Trait`,
+    /// `dyn Trait` or if a method call on a trait object introduces a static requirement.
+    pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
+        debug!("try_report_static_impl_trait(error={:?})", self.error);
+        let tcx = self.tcx();
+        let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? {
+            RegionResolutionError::SubSupConflict(
+                _,
+                var_origin,
+                sub_origin,
+                sub_r,
+                sup_origin,
+                sup_r,
+            ) if **sub_r == RegionKind::ReStatic => {
+                (var_origin, sub_origin, sub_r, sup_origin, sup_r)
+            }
+            RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
+                sub_r,
+                sup_r,
+            ) if **sub_r == RegionKind::ReStatic => {
+                // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
+                if let ObligationCauseCode::UnifyReceiver(ctxt) = &cause.code {
+                    // This may have a closure and it would cause ICE
+                    // through `find_param_with_region` (#78262).
+                    let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
+                    let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
+                    if fn_returns.is_empty() {
+                        return None;
+                    }
+
+                    let param = self.find_param_with_region(sup_r, sub_r)?;
+                    let lifetime = if sup_r.has_name() {
+                        format!("lifetime `{}`", sup_r)
+                    } else {
+                        "an anonymous lifetime `'_`".to_string()
+                    };
+                    let mut err = struct_span_err!(
+                        tcx.sess,
+                        cause.span,
+                        E0772,
+                        "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
+                         requirement",
+                        param
+                            .param
+                            .pat
+                            .simple_ident()
+                            .map(|s| format!("`{}`", s))
+                            .unwrap_or_else(|| "`fn` parameter".to_string()),
+                        lifetime,
+                        ctxt.assoc_item.ident,
+                    );
+                    err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
+                    err.span_label(
+                        cause.span,
+                        &format!(
+                            "...is captured and required to live as long as `'static` here \
+                             because of an implicit lifetime bound on the {}",
+                            match ctxt.assoc_item.container {
+                                AssocItemContainer::TraitContainer(id) =>
+                                    format!("`impl` of `{}`", tcx.def_path_str(id)),
+                                AssocItemContainer::ImplContainer(_) =>
+                                    "inherent `impl`".to_string(),
+                            },
+                        ),
+                    );
+                    if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
+                        err.emit();
+                        return Some(ErrorReported);
+                    } else {
+                        err.cancel();
+                    }
+                }
+                return None;
+            }
+            _ => return None,
+        };
+        debug!(
+            "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
+            var_origin, sub_origin, sub_r, sup_origin, sup_r
+        );
+        let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
+        debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
+        let sp = var_origin.span();
+        let return_sp = sub_origin.span();
+        let param = self.find_param_with_region(sup_r, sub_r)?;
+        let (lifetime_name, lifetime) = if sup_r.has_name() {
+            (sup_r.to_string(), format!("lifetime `{}`", sup_r))
+        } else {
+            ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
+        };
+        let param_name = param
+            .param
+            .pat
+            .simple_ident()
+            .map(|s| format!("`{}`", s))
+            .unwrap_or_else(|| "`fn` parameter".to_string());
+        let mut err = struct_span_err!(
+            tcx.sess,
+            sp,
+            E0759,
+            "{} has {} but it needs to satisfy a `'static` lifetime requirement",
+            param_name,
+            lifetime,
+        );
+        err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
+        debug!("try_report_static_impl_trait: param_info={:?}", param);
+
+        // We try to make the output have fewer overlapping spans if possible.
+        if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span()))
+            && sup_origin.span() != return_sp
+        {
+            // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs`
+
+            // Customize the spans and labels depending on their relative order so
+            // that split sentences flow correctly.
+            if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() {
+                // Avoid the following:
+                //
+                // error: cannot infer an appropriate lifetime
+                //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
+                //    |
+                // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
+                //    |           ----                      ---------^-
+                //
+                // and instead show:
+                //
+                // error: cannot infer an appropriate lifetime
+                //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
+                //    |
+                // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
+                //    |           ----                               ^
+                err.span_label(
+                    sup_origin.span(),
+                    "...is captured here, requiring it to live as long as `'static`",
+                );
+            } else {
+                err.span_label(sup_origin.span(), "...is captured here...");
+                if return_sp < sup_origin.span() {
+                    err.span_note(
+                        return_sp,
+                        "...and is required to live as long as `'static` here",
+                    );
+                } else {
+                    err.span_label(
+                        return_sp,
+                        "...and is required to live as long as `'static` here",
+                    );
+                }
+            }
+        } else {
+            err.span_label(
+                return_sp,
+                "...is captured and required to live as long as `'static` here",
+            );
+        }
+
+        let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
+
+        let mut override_error_code = None;
+        if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin {
+            if let ObligationCauseCode::UnifyReceiver(ctxt) = &cause.code {
+                // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
+                // `'static` lifetime when called as a method on a binding: `bar.qux()`.
+                if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
+                    override_error_code = Some(ctxt.assoc_item.ident);
+                }
+            }
+        }
+        if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin {
+            let code = match &cause.code {
+                ObligationCauseCode::MatchImpl(parent, ..) => &**parent,
+                _ => &cause.code,
+            };
+            if let ObligationCauseCode::ItemObligation(item_def_id) = *code {
+                // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
+                // lifetime as above, but called using a fully-qualified path to the method:
+                // `Foo::qux(bar)`.
+                let mut v = TraitObjectVisitor(FxHashSet::default());
+                v.visit_ty(param.param_ty);
+                if let Some((ident, self_ty)) =
+                    self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0)
+                {
+                    if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) {
+                        override_error_code = Some(ident);
+                    }
+                }
+            }
+        }
+        if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
+            // Provide a more targeted error code and description.
+            err.code(rustc_errors::error_code!(E0772));
+            err.set_primary_message(&format!(
+                "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
+                requirement",
+                param_name, lifetime, ident,
+            ));
+        }
+
+        debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
+        // FIXME: account for the need of parens in `&(dyn Trait + '_)`
+        let consider = "consider changing the";
+        let declare = "to declare that the";
+        let arg = match param.param.pat.simple_ident() {
+            Some(simple_ident) => format!("argument `{}`", simple_ident),
+            None => "the argument".to_string(),
+        };
+        let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
+        let explicit_static = format!("explicit `'static` bound to the lifetime of {}", arg);
+        let captures = format!("captures data from {}", arg);
+        let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
+        let plus_lt = format!(" + {}", lifetime_name);
+        for fn_return in fn_returns {
+            if fn_return.span.desugaring_kind().is_some() {
+                // Skip `async` desugaring `impl Future`.
+                continue;
+            }
+            match fn_return.kind {
+                TyKind::OpaqueDef(item_id, _) => {
+                    let item = tcx.hir().item(item_id);
+                    let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind {
+                        opaque
+                    } else {
+                        err.emit();
+                        return Some(ErrorReported);
+                    };
+
+                    if let Some(span) = opaque
+                        .bounds
+                        .iter()
+                        .filter_map(|arg| match arg {
+                            GenericBound::Outlives(Lifetime {
+                                name: LifetimeName::Static,
+                                span,
+                                ..
+                            }) => Some(*span),
+                            _ => None,
+                        })
+                        .next()
+                    {
+                        err.span_suggestion_verbose(
+                            span,
+                            &format!("{} `impl Trait`'s {}", consider, explicit_static),
+                            lifetime_name.clone(),
+                            Applicability::MaybeIncorrect,
+                        );
+                        err.span_suggestion_verbose(
+                            param.param_ty_span,
+                            add_static_bound,
+                            param.param_ty.to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else if opaque
+                        .bounds
+                        .iter()
+                        .filter_map(|arg| match arg {
+                            GenericBound::Outlives(Lifetime { name, span, .. })
+                                if name.ident().to_string() == lifetime_name =>
+                            {
+                                Some(*span)
+                            }
+                            _ => None,
+                        })
+                        .next()
+                        .is_some()
+                    {
+                    } else {
+                        err.span_suggestion_verbose(
+                            fn_return.span.shrink_to_hi(),
+                            &format!(
+                                "{declare} `impl Trait` {captures}, {explicit}",
+                                declare = declare,
+                                captures = captures,
+                                explicit = explicit,
+                            ),
+                            plus_lt.clone(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+                TyKind::TraitObject(_, lt, _) => match lt.name {
+                    LifetimeName::ImplicitObjectLifetimeDefault => {
+                        err.span_suggestion_verbose(
+                            fn_return.span.shrink_to_hi(),
+                            &format!(
+                                "{declare} trait object {captures}, {explicit}",
+                                declare = declare,
+                                captures = captures,
+                                explicit = explicit,
+                            ),
+                            plus_lt.clone(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    name if name.ident().to_string() != lifetime_name => {
+                        // With this check we avoid suggesting redundant bounds. This
+                        // would happen if there are nested impl/dyn traits and only
+                        // one of them has the bound we'd suggest already there, like
+                        // in `impl Foo<X = dyn Bar> + '_`.
+                        err.span_suggestion_verbose(
+                            lt.span,
+                            &format!("{} trait object's {}", consider, explicit_static),
+                            lifetime_name.clone(),
+                            Applicability::MaybeIncorrect,
+                        );
+                        err.span_suggestion_verbose(
+                            param.param_ty_span,
+                            add_static_bound,
+                            param.param_ty.to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    _ => {}
+                },
+                _ => {}
+            }
+        }
+        err.emit();
+        Some(ErrorReported)
+    }
+
+    fn get_impl_ident_and_self_ty_from_trait(
+        &self,
+        def_id: DefId,
+        trait_objects: &FxHashSet<DefId>,
+    ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
+        let tcx = self.tcx();
+        match tcx.hir().get_if_local(def_id) {
+            Some(Node::ImplItem(impl_item)) => {
+                match tcx.hir().find(tcx.hir().get_parent_item(impl_item.hir_id())) {
+                    Some(Node::Item(Item {
+                        kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
+                        ..
+                    })) => Some((impl_item.ident, self_ty)),
+                    _ => None,
+                }
+            }
+            Some(Node::TraitItem(trait_item)) => {
+                let parent_id = tcx.hir().get_parent_item(trait_item.hir_id());
+                match tcx.hir().find(parent_id) {
+                    Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => {
+                        // The method being called is defined in the `trait`, but the `'static`
+                        // obligation comes from the `impl`. Find that `impl` so that we can point
+                        // at it in the suggestion.
+                        let trait_did = tcx.hir().local_def_id(parent_id).to_def_id();
+                        match tcx
+                            .hir()
+                            .trait_impls(trait_did)
+                            .iter()
+                            .filter_map(|&impl_did| {
+                                match tcx.hir().get_if_local(impl_did.to_def_id()) {
+                                    Some(Node::Item(Item {
+                                        kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
+                                        ..
+                                    })) if trait_objects.iter().all(|did| {
+                                        // FIXME: we should check `self_ty` against the receiver
+                                        // type in the `UnifyReceiver` context, but for now, use
+                                        // this imperfect proxy. This will fail if there are
+                                        // multiple `impl`s for the same trait like
+                                        // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
+                                        // In that case, only the first one will get suggestions.
+                                        let mut traits = vec![];
+                                        let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
+                                        hir_v.visit_ty(self_ty);
+                                        !traits.is_empty()
+                                    }) =>
+                                    {
+                                        Some(self_ty)
+                                    }
+                                    _ => None,
+                                }
+                            })
+                            .next()
+                        {
+                            Some(self_ty) => Some((trait_item.ident, self_ty)),
+                            _ => None,
+                        }
+                    }
+                    _ => None,
+                }
+            }
+            _ => None,
+        }
+    }
+
+    /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
+    /// `'static` obligation. Suggest relaxing that implicit bound.
+    fn find_impl_on_dyn_trait(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        ty: Ty<'_>,
+        ctxt: &UnifyReceiverContext<'tcx>,
+    ) -> bool {
+        let tcx = self.tcx();
+
+        // Find the method being called.
+        let instance = match ty::Instance::resolve(
+            tcx,
+            ctxt.param_env,
+            ctxt.assoc_item.def_id,
+            self.infcx.resolve_vars_if_possible(ctxt.substs),
+        ) {
+            Ok(Some(instance)) => instance,
+            _ => return false,
+        };
+
+        let mut v = TraitObjectVisitor(FxHashSet::default());
+        v.visit_ty(ty);
+
+        // Get the `Ident` of the method being called and the corresponding `impl` (to point at
+        // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
+        let (ident, self_ty) =
+            match self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0) {
+                Some((ident, self_ty)) => (ident, self_ty),
+                None => return false,
+            };
+
+        // Find the trait object types in the argument, so we point at *only* the trait object.
+        self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty)
+    }
+
+    fn suggest_constrain_dyn_trait_in_impl(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        found_dids: &FxHashSet<DefId>,
+        ident: Ident,
+        self_ty: &hir::Ty<'_>,
+    ) -> bool {
+        let mut suggested = false;
+        for found_did in found_dids {
+            let mut traits = vec![];
+            let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
+            hir_v.visit_ty(&self_ty);
+            for span in &traits {
+                let mut multi_span: MultiSpan = vec![*span].into();
+                multi_span.push_span_label(
+                    *span,
+                    "this has an implicit `'static` lifetime requirement".to_string(),
+                );
+                multi_span.push_span_label(
+                    ident.span,
+                    "calling this method introduces the `impl`'s 'static` requirement".to_string(),
+                );
+                err.span_note(multi_span, "the used `impl` has a `'static` requirement");
+                err.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    "consider relaxing the implicit `'static` requirement",
+                    " + '_".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+                suggested = true;
+            }
+        }
+        suggested
+    }
+}
+
+/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
+pub(super) struct TraitObjectVisitor(pub(super) FxHashSet<DefId>);
+
+impl TypeVisitor<'_> for TraitObjectVisitor {
+    fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow<Self::BreakTy> {
+        match t.kind() {
+            ty::Dynamic(preds, RegionKind::ReStatic) => {
+                if let Some(def_id) = preds.principal_def_id() {
+                    self.0.insert(def_id);
+                }
+                ControlFlow::CONTINUE
+            }
+            _ => t.super_visit_with(self),
+        }
+    }
+}
+
+/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
+pub(super) struct HirTraitObjectVisitor<'a>(pub(super) &'a mut Vec<Span>, pub(super) DefId);
+
+impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
+    type Map = ErasedMap<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
+        if let TyKind::TraitObject(
+            poly_trait_refs,
+            Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
+            _,
+        ) = t.kind
+        {
+            for ptr in poly_trait_refs {
+                if Some(self.1) == ptr.trait_ref.trait_def_id() {
+                    self.0.push(ptr.span);
+                }
+            }
+        }
+        walk_ty(self, t);
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
new file mode 100644
index 00000000000..61c8113d052
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
@@ -0,0 +1,151 @@
+//! Error Reporting for `impl` items that do not match the obligations from their `trait`.
+
+use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::lexical_region_resolve::RegionResolutionError;
+use crate::infer::{Subtype, ValuePairs};
+use crate::traits::ObligationCauseCode::CompareImplMethodObligation;
+use rustc_errors::ErrorReported;
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
+use rustc_middle::ty::error::ExpectedFound;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::{MultiSpan, Span};
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
+    pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorReported> {
+        if let Some(ref error) = self.error {
+            debug!("try_report_impl_not_conforming_to_trait {:?}", error);
+            if let RegionResolutionError::SubSupConflict(
+                _,
+                var_origin,
+                sub_origin,
+                _sub,
+                sup_origin,
+                _sup,
+            ) = error.clone()
+            {
+                if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) =
+                    (&sup_origin, &sub_origin)
+                {
+                    if let (
+                        ValuePairs::Types(sub_expected_found),
+                        ValuePairs::Types(sup_expected_found),
+                        CompareImplMethodObligation { trait_item_def_id, .. },
+                    ) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code)
+                    {
+                        if sup_expected_found == sub_expected_found {
+                            self.emit_err(
+                                var_origin.span(),
+                                sub_expected_found.expected,
+                                sub_expected_found.found,
+                                *trait_item_def_id,
+                            );
+                            return Some(ErrorReported);
+                        }
+                    }
+                }
+            }
+        }
+        None
+    }
+
+    fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, trait_def_id: DefId) {
+        let trait_sp = self.tcx().def_span(trait_def_id);
+        let mut err = self
+            .tcx()
+            .sess
+            .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature");
+        err.span_label(sp, &format!("found `{}`", found));
+        err.span_label(trait_sp, &format!("expected `{}`", expected));
+
+        // Get the span of all the used type parameters in the method.
+        let assoc_item = self.tcx().associated_item(trait_def_id);
+        let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
+        match assoc_item.kind {
+            ty::AssocKind::Fn => {
+                let hir = self.tcx().hir();
+                if let Some(hir_id) =
+                    assoc_item.def_id.as_local().map(|id| hir.local_def_id_to_hir_id(id))
+                {
+                    if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) {
+                        visitor.visit_fn_decl(decl);
+                    }
+                }
+            }
+            _ => {}
+        }
+        let mut type_param_span: MultiSpan = visitor.types.to_vec().into();
+        for &span in &visitor.types {
+            type_param_span.push_span_label(
+                span,
+                "consider borrowing this type parameter in the trait".to_string(),
+            );
+        }
+
+        if let Some((expected, found)) =
+            self.infcx.expected_found_str_ty(ExpectedFound { expected, found })
+        {
+            // Highlighted the differences when showing the "expected/found" note.
+            err.note_expected_found(&"", expected, &"", found);
+        } else {
+            // This fallback shouldn't be necessary, but let's keep it in just in case.
+            err.note(&format!("expected `{}`\n   found `{}`", expected, found));
+        }
+        err.span_help(
+            type_param_span,
+            "the lifetime requirements from the `impl` do not correspond to the requirements in \
+             the `trait`",
+        );
+        if visitor.types.is_empty() {
+            err.help(
+                "verify the lifetime relationships in the `trait` and `impl` between the `self` \
+                 argument, the other inputs and its output",
+            );
+        }
+        err.emit();
+    }
+}
+
+struct TypeParamSpanVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    types: Vec<Span>,
+}
+
+impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
+    type Map = rustc_middle::hir::map::Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
+        hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir())
+    }
+
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
+        match arg.kind {
+            hir::TyKind::Rptr(_, ref mut_ty) => {
+                // We don't want to suggest looking into borrowing `&T` or `&Self`.
+                hir::intravisit::walk_ty(self, mut_ty.ty);
+                return;
+            }
+            hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
+                [segment]
+                    if segment
+                        .res
+                        .map(|res| {
+                            matches!(
+                                res,
+                                Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _)
+                            )
+                        })
+                        .unwrap_or(false) =>
+                {
+                    self.types.push(path.span);
+                }
+                _ => {}
+            },
+            _ => {}
+        }
+        hir::intravisit::walk_ty(self, arg);
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
new file mode 100644
index 00000000000..8dcdd4b149e
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -0,0 +1,184 @@
+//! Helper functions corresponding to lifetime errors due to
+//! anonymous regions.
+
+use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
+use rustc_middle::ty::{self, DefIdTree, Region, Ty};
+use rustc_span::Span;
+
+/// Information about the anonymous region we are searching for.
+#[derive(Debug)]
+pub(super) struct AnonymousParamInfo<'tcx> {
+    /// The parameter corresponding to the anonymous region.
+    pub param: &'tcx hir::Param<'tcx>,
+    /// The type corresponding to the anonymous region parameter.
+    pub param_ty: Ty<'tcx>,
+    /// The ty::BoundRegionKind corresponding to the anonymous region.
+    pub bound_region: ty::BoundRegionKind,
+    /// The `Span` of the parameter type.
+    pub param_ty_span: Span,
+    /// Signals that the argument is the first parameter in the declaration.
+    pub is_first: bool,
+}
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    // This method walks the Type of the function body parameters using
+    // `fold_regions()` function and returns the
+    // &hir::Param of the function parameter corresponding to the anonymous
+    // region and the Ty corresponding to the named region.
+    // Currently only the case where the function declaration consists of
+    // one named region and one anonymous region is handled.
+    // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
+    // Here, we would return the hir::Param for y, we return the type &'a
+    // i32, which is the type of y but with the anonymous region replaced
+    // with 'a, the corresponding bound region and is_first which is true if
+    // the hir::Param is the first parameter in the function declaration.
+    pub(super) fn find_param_with_region(
+        &self,
+        anon_region: Region<'tcx>,
+        replace_region: Region<'tcx>,
+    ) -> Option<AnonymousParamInfo<'_>> {
+        let (id, bound_region) = match *anon_region {
+            ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
+            ty::ReEarlyBound(ebr) => (
+                self.tcx().parent(ebr.def_id).unwrap(),
+                ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
+            ),
+            _ => return None, // not a free region
+        };
+
+        let hir = &self.tcx().hir();
+        let hir_id = hir.local_def_id_to_hir_id(id.as_local()?);
+        let body_id = hir.maybe_body_owned_by(hir_id)?;
+        let body = hir.body(body_id);
+        let owner_id = hir.body_owner(body_id);
+        let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap();
+        let poly_fn_sig = self.tcx().fn_sig(id);
+        let fn_sig = self.tcx().liberate_late_bound_regions(id, poly_fn_sig);
+        body.params
+            .iter()
+            .take(if fn_sig.c_variadic {
+                fn_sig.inputs().len()
+            } else {
+                assert_eq!(fn_sig.inputs().len(), body.params.len());
+                body.params.len()
+            })
+            .enumerate()
+            .find_map(|(index, param)| {
+                // May return None; sometimes the tables are not yet populated.
+                let ty = fn_sig.inputs()[index];
+                let mut found_anon_region = false;
+                let new_param_ty = self.tcx().fold_regions(ty, &mut false, |r, _| {
+                    if *r == *anon_region {
+                        found_anon_region = true;
+                        replace_region
+                    } else {
+                        r
+                    }
+                });
+                if found_anon_region {
+                    let ty_hir_id = fn_decl.inputs[index].hir_id;
+                    let param_ty_span = hir.span(ty_hir_id);
+                    let is_first = index == 0;
+                    Some(AnonymousParamInfo {
+                        param,
+                        param_ty: new_param_ty,
+                        param_ty_span,
+                        bound_region,
+                        is_first,
+                    })
+                } else {
+                    None
+                }
+            })
+    }
+
+    pub(super) fn future_return_type(
+        &self,
+        local_def_id: LocalDefId,
+    ) -> Option<&rustc_hir::Ty<'_>> {
+        if let Some(hir::IsAsync::Async) = self.asyncness(local_def_id) {
+            if let rustc_middle::ty::Opaque(def_id, _) =
+                self.tcx().type_of(local_def_id).fn_sig(self.tcx()).output().skip_binder().kind()
+            {
+                match self.tcx().hir().get_if_local(*def_id) {
+                    Some(hir::Node::Item(hir::Item {
+                        kind:
+                            hir::ItemKind::OpaqueTy(hir::OpaqueTy {
+                                bounds,
+                                origin: hir::OpaqueTyOrigin::AsyncFn,
+                                ..
+                            }),
+                        ..
+                    })) => {
+                        for b in bounds.iter() {
+                            if let hir::GenericBound::LangItemTrait(
+                                hir::LangItem::Future,
+                                _span,
+                                _hir_id,
+                                generic_args,
+                            ) = b
+                            {
+                                for type_binding in generic_args.bindings.iter() {
+                                    if type_binding.ident.name == rustc_span::sym::Output {
+                                        if let hir::TypeBindingKind::Equality { ty } =
+                                            type_binding.kind
+                                        {
+                                            return Some(ty);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
+        None
+    }
+
+    pub(super) fn asyncness(&self, local_def_id: LocalDefId) -> Option<hir::IsAsync> {
+        // similar to the asyncness fn in rustc_ty_utils::ty
+        let hir_id = self.tcx().hir().local_def_id_to_hir_id(local_def_id);
+        let node = self.tcx().hir().get(hir_id);
+        let fn_like = rustc_middle::hir::map::blocks::FnLikeNode::from_node(node)?;
+
+        Some(fn_like.asyncness())
+    }
+
+    // Here, we check for the case where the anonymous region
+    // is in the return type.
+    // FIXME(#42703) - Need to handle certain cases here.
+    pub(super) fn is_return_type_anon(
+        &self,
+        scope_def_id: LocalDefId,
+        br: ty::BoundRegionKind,
+        decl: &hir::FnDecl<'_>,
+    ) -> Option<Span> {
+        let ret_ty = self.tcx().type_of(scope_def_id);
+        if let ty::FnDef(_, _) = ret_ty.kind() {
+            let sig = ret_ty.fn_sig(self.tcx());
+            let late_bound_regions =
+                self.tcx().collect_referenced_late_bound_regions(&sig.output());
+            if late_bound_regions.iter().any(|r| *r == br) {
+                return Some(decl.output.span());
+            }
+        }
+        None
+    }
+
+    // Here we check for the case where anonymous region
+    // corresponds to self and if yes, we display E0312.
+    // FIXME(#42700) - Need to format self properly to
+    // enable E0621 for it.
+    pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: LocalDefId) -> bool {
+        is_first
+            && self
+                .tcx()
+                .opt_associated_item(scope_def_id.to_def_id())
+                .map(|i| i.fn_has_self_parameter)
+                == Some(true)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
new file mode 100644
index 00000000000..4bc59a4baf5
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -0,0 +1,396 @@
+use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt};
+use crate::infer::{self, InferCtxt, SubregionOrigin};
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::{self, Region};
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    pub(super) fn note_region_origin(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        origin: &SubregionOrigin<'tcx>,
+    ) {
+        let mut label_or_note = |span, msg| {
+            let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count();
+            let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count();
+            let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span);
+            if span_is_primary && sub_count == 0 && expanded_sub_count == 0 {
+                err.span_label(span, msg);
+            } else if span_is_primary && expanded_sub_count == 0 {
+                err.note(msg);
+            } else {
+                err.span_note(span, msg);
+            }
+        };
+        match *origin {
+            infer::Subtype(ref trace) => {
+                if let Some((expected, found)) = self.values_str(trace.values) {
+                    label_or_note(
+                        trace.cause.span,
+                        &format!("...so that the {}", trace.cause.as_requirement_str()),
+                    );
+
+                    err.note_expected_found(&"", expected, &"", found);
+                } else {
+                    // FIXME: this really should be handled at some earlier stage. Our
+                    // handling of region checking when type errors are present is
+                    // *terrible*.
+
+                    label_or_note(
+                        trace.cause.span,
+                        &format!("...so that {}", trace.cause.as_requirement_str()),
+                    );
+                }
+            }
+            infer::Reborrow(span) => {
+                label_or_note(span, "...so that reference does not outlive borrowed content");
+            }
+            infer::ReborrowUpvar(span, ref upvar_id) => {
+                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+                label_or_note(span, &format!("...so that closure can access `{}`", var_name));
+            }
+            infer::RelateObjectBound(span) => {
+                label_or_note(span, "...so that it can be closed over into an object");
+            }
+            infer::CallReturn(span) => {
+                label_or_note(span, "...so that return value is valid for the call");
+            }
+            infer::DataBorrowed(ty, span) => {
+                label_or_note(
+                    span,
+                    &format!(
+                        "...so that the type `{}` is not borrowed for too long",
+                        self.ty_to_string(ty)
+                    ),
+                );
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                label_or_note(
+                    span,
+                    &format!(
+                        "...so that the reference type `{}` does not outlive the data it points at",
+                        self.ty_to_string(ty)
+                    ),
+                );
+            }
+            infer::RelateParamBound(span, t, opt_span) => {
+                label_or_note(
+                    span,
+                    &format!(
+                        "...so that the type `{}` will meet its required lifetime bounds{}",
+                        self.ty_to_string(t),
+                        if opt_span.is_some() { "..." } else { "" },
+                    ),
+                );
+                if let Some(span) = opt_span {
+                    err.span_note(span, "...that is required by this bound");
+                }
+            }
+            infer::RelateRegionParamBound(span) => {
+                label_or_note(
+                    span,
+                    "...so that the declared lifetime parameter bounds are satisfied",
+                );
+            }
+            infer::CompareImplMethodObligation { span, .. } => {
+                label_or_note(
+                    span,
+                    "...so that the definition in impl matches the definition from the trait",
+                );
+            }
+        }
+    }
+
+    pub(super) fn report_concrete_failure(
+        &self,
+        origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        match origin {
+            infer::Subtype(box trace) => {
+                let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
+                let mut err = self.report_and_explain_type_error(trace, &terr);
+                match (sub, sup) {
+                    (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
+                    (ty::RePlaceholder(_), _) => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            "",
+                            sup,
+                            " doesn't meet the lifetime requirements",
+                            None,
+                        );
+                    }
+                    (_, ty::RePlaceholder(_)) => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            "the required lifetime does not necessarily outlive ",
+                            sub,
+                            "",
+                            None,
+                        );
+                    }
+                    _ => {
+                        note_and_explain_region(self.tcx, &mut err, "", sup, "...", None);
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            "...does not necessarily outlive ",
+                            sub,
+                            "",
+                            None,
+                        );
+                    }
+                }
+                err
+            }
+            infer::Reborrow(span) => {
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0312,
+                    "lifetime of reference outlives lifetime of borrowed content..."
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "...the reference is valid for ",
+                    sub,
+                    "...",
+                    None,
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "...but the borrowed content is only valid for ",
+                    sup,
+                    "",
+                    None,
+                );
+                err
+            }
+            infer::ReborrowUpvar(span, ref upvar_id) => {
+                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0313,
+                    "lifetime of borrowed pointer outlives lifetime of captured variable `{}`...",
+                    var_name
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "...the borrowed pointer is valid for ",
+                    sub,
+                    "...",
+                    None,
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    &format!("...but `{}` is only valid for ", var_name),
+                    sup,
+                    "",
+                    None,
+                );
+                err
+            }
+            infer::RelateObjectBound(span) => {
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0476,
+                    "lifetime of the source pointer does not outlive lifetime bound of the \
+                     object type"
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "object type is valid for ",
+                    sub,
+                    "",
+                    None,
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "source pointer is only valid for ",
+                    sup,
+                    "",
+                    None,
+                );
+                err
+            }
+            infer::RelateParamBound(span, ty, opt_span) => {
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0477,
+                    "the type `{}` does not fulfill the required lifetime",
+                    self.ty_to_string(ty)
+                );
+                match *sub {
+                    ty::ReStatic => note_and_explain_region(
+                        self.tcx,
+                        &mut err,
+                        "type must satisfy ",
+                        sub,
+                        if opt_span.is_some() { " as required by this binding" } else { "" },
+                        opt_span,
+                    ),
+                    _ => note_and_explain_region(
+                        self.tcx,
+                        &mut err,
+                        "type must outlive ",
+                        sub,
+                        if opt_span.is_some() { " as required by this binding" } else { "" },
+                        opt_span,
+                    ),
+                }
+                err
+            }
+            infer::RelateRegionParamBound(span) => {
+                let mut err =
+                    struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "lifetime parameter instantiated with ",
+                    sup,
+                    "",
+                    None,
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "but lifetime parameter must outlive ",
+                    sub,
+                    "",
+                    None,
+                );
+                err
+            }
+            infer::CallReturn(span) => {
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0482,
+                    "lifetime of return value does not outlive the function call"
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "the return value is only valid for ",
+                    sup,
+                    "",
+                    None,
+                );
+                err
+            }
+            infer::DataBorrowed(ty, span) => {
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0490,
+                    "a value of type `{}` is borrowed for too long",
+                    self.ty_to_string(ty)
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "the type is valid for ",
+                    sub,
+                    "",
+                    None,
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "but the borrow lasts for ",
+                    sup,
+                    "",
+                    None,
+                );
+                err
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                let mut err = struct_span_err!(
+                    self.tcx.sess,
+                    span,
+                    E0491,
+                    "in type `{}`, reference has a longer lifetime than the data it references",
+                    self.ty_to_string(ty)
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "the pointer is valid for ",
+                    sub,
+                    "",
+                    None,
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "but the referenced data is only valid for ",
+                    sup,
+                    "",
+                    None,
+                );
+                err
+            }
+            infer::CompareImplMethodObligation {
+                span,
+                item_name,
+                impl_item_def_id,
+                trait_item_def_id,
+            } => self.report_extra_impl_obligation(
+                span,
+                item_name,
+                impl_item_def_id,
+                trait_item_def_id,
+                &format!("`{}: {}`", sup, sub),
+            ),
+        }
+    }
+
+    pub(super) fn report_placeholder_failure(
+        &self,
+        placeholder_origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        // I can't think how to do better than this right now. -nikomatsakis
+        debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
+        match placeholder_origin {
+            infer::Subtype(box ref trace)
+                if matches!(
+                    &trace.cause.code.peel_derives(),
+                    ObligationCauseCode::BindingObligation(..)
+                ) =>
+            {
+                // Hack to get around the borrow checker because trace.cause has an `Rc`.
+                if let ObligationCauseCode::BindingObligation(_, span) =
+                    &trace.cause.code.peel_derives()
+                {
+                    let span = *span;
+                    let mut err = self.report_concrete_failure(placeholder_origin, sub, sup);
+                    err.span_note(span, "the lifetime requirement is introduced here");
+                    err
+                } else {
+                    unreachable!()
+                }
+            }
+            infer::Subtype(box trace) => {
+                let terr = TypeError::RegionsPlaceholderMismatch;
+                return self.report_and_explain_type_error(trace, &terr);
+            }
+            _ => return self.report_concrete_failure(placeholder_origin, sub, sup),
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs
new file mode 100644
index 00000000000..728dc2de370
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/free_regions.rs
@@ -0,0 +1,160 @@
+//! This module handles the relationships between "free regions", i.e., lifetime parameters.
+//! Ordinarily, free regions are unrelated to one another, but they can be related via implied
+//! or explicit bounds. In that case, we track the bounds using the `TransitiveRelation` type,
+//! and use that to decide when one free region outlives another, and so forth.
+
+use rustc_data_structures::transitive_relation::TransitiveRelation;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::{self, Lift, Region, TyCtxt};
+
+/// Combines a `FreeRegionMap` and a `TyCtxt`.
+///
+/// This stuff is a bit convoluted and should be refactored, but as we
+/// transition to NLL, it'll all go away anyhow.
+pub struct RegionRelations<'a, 'tcx> {
+    pub tcx: TyCtxt<'tcx>,
+
+    /// The context used for debug messages
+    pub context: DefId,
+
+    /// Free-region relationships.
+    pub free_regions: &'a FreeRegionMap<'tcx>,
+}
+
+impl<'a, 'tcx> RegionRelations<'a, 'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>, context: DefId, free_regions: &'a FreeRegionMap<'tcx>) -> Self {
+        Self { tcx, context, free_regions }
+    }
+
+    pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> {
+        self.free_regions.lub_free_regions(self.tcx, r_a, r_b)
+    }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct FreeRegionMap<'tcx> {
+    // Stores the relation `a < b`, where `a` and `b` are regions.
+    //
+    // Invariant: only free regions like `'x` or `'static` are stored
+    // in this relation, not scopes.
+    relation: TransitiveRelation<Region<'tcx>>,
+}
+
+impl<'tcx> FreeRegionMap<'tcx> {
+    pub fn elements(&self) -> impl Iterator<Item = &Region<'tcx>> {
+        self.relation.elements()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.relation.is_empty()
+    }
+
+    // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
+    // (with the exception that `'static: 'x` is not notable)
+    pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
+        debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
+        if self.is_free_or_static(sub) && self.is_free(sup) {
+            self.relation.add(sub, sup)
+        }
+    }
+
+    /// Tests whether `r_a <= r_b`.
+    ///
+    /// Both regions must meet `is_free_or_static`.
+    ///
+    /// Subtle: one tricky case that this code gets correct is as
+    /// follows. If we know that `r_b: 'static`, then this function
+    /// will return true, even though we don't know anything that
+    /// directly relates `r_a` and `r_b`.
+    ///
+    /// Also available through the `FreeRegionRelations` trait below.
+    pub fn sub_free_regions(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        r_a: Region<'tcx>,
+        r_b: Region<'tcx>,
+    ) -> bool {
+        assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b));
+        let re_static = tcx.lifetimes.re_static;
+        if self.check_relation(re_static, r_b) {
+            // `'a <= 'static` is always true, and not stored in the
+            // relation explicitly, so check if `'b` is `'static` (or
+            // equivalent to it)
+            true
+        } else {
+            self.check_relation(r_a, r_b)
+        }
+    }
+
+    /// Check whether `r_a <= r_b` is found in the relation.
+    fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
+        r_a == r_b || self.relation.contains(&r_a, &r_b)
+    }
+
+    /// True for free regions other than `'static`.
+    pub fn is_free(&self, r: Region<'_>) -> bool {
+        matches!(r, ty::ReEarlyBound(_) | ty::ReFree(_))
+    }
+
+    /// True if `r` is a free region or static of the sort that this
+    /// free region map can be used with.
+    pub fn is_free_or_static(&self, r: Region<'_>) -> bool {
+        match *r {
+            ty::ReStatic => true,
+            _ => self.is_free(r),
+        }
+    }
+
+    /// Computes the least-upper-bound of two free regions. In some
+    /// cases, this is more conservative than necessary, in order to
+    /// avoid making arbitrary choices. See
+    /// `TransitiveRelation::postdom_upper_bound` for more details.
+    pub fn lub_free_regions(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        r_a: Region<'tcx>,
+        r_b: Region<'tcx>,
+    ) -> Region<'tcx> {
+        debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
+        assert!(self.is_free(r_a));
+        assert!(self.is_free(r_b));
+        let result = if r_a == r_b {
+            r_a
+        } else {
+            match self.relation.postdom_upper_bound(&r_a, &r_b) {
+                None => tcx.lifetimes.re_static,
+                Some(r) => *r,
+            }
+        };
+        debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
+        result
+    }
+}
+
+/// The NLL region handling code represents free region relations in a
+/// slightly different way; this trait allows functions to be abstract
+/// over which version is in use.
+pub trait FreeRegionRelations<'tcx> {
+    /// Tests whether `r_a <= r_b`. Both must be free regions or
+    /// `'static`.
+    fn sub_free_regions(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        shorter: ty::Region<'tcx>,
+        longer: ty::Region<'tcx>,
+    ) -> bool;
+}
+
+impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
+    fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
+        // invoke the "inherent method"
+        self.sub_free_regions(tcx, r_a, r_b)
+    }
+}
+
+impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
+    type Lifted = FreeRegionMap<'tcx>;
+    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> {
+        self.relation.maybe_map(|&fr| tcx.lift(fr)).map(|relation| FreeRegionMap { relation })
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
new file mode 100644
index 00000000000..4af1bdf97a7
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -0,0 +1,266 @@
+//! Freshening is the process of replacing unknown variables with fresh types. The idea is that
+//! the type, after freshening, contains no inference variables but instead contains either a
+//! value for each variable or fresh "arbitrary" types wherever a variable would have been.
+//!
+//! Freshening is used primarily to get a good type for inserting into a cache. The result
+//! summarizes what the type inferencer knows "so far". The primary place it is used right now is
+//! in the trait matching algorithm, which needs to be able to cache whether an `impl` self type
+//! matches some other type X -- *without* affecting `X`. That means if that if the type `X` is in
+//! fact an unbound type variable, we want the match to be regarded as ambiguous, because depending
+//! on what type that type variable is ultimately assigned, the match may or may not succeed.
+//!
+//! To handle closures, freshened types also have to contain the signature and kind of any
+//! closure in the local inference context, as otherwise the cache key might be invalidated.
+//! The way this is done is somewhat hacky - the closure signature is appended to the substs,
+//! as well as the closure kind "encoded" as a type. Also, special handling is needed when
+//! the closure signature contains a reference to the original closure.
+//!
+//! Note that you should be careful not to allow the output of freshening to leak to the user in
+//! error messages or in any other form. Freshening is only really useful as an internal detail.
+//!
+//! Because of the manipulation required to handle closures, doing arbitrary operations on
+//! freshened types is not recommended. However, in addition to doing equality/hash
+//! comparisons (for caching), it is possible to do a `ty::_match` operation between
+//! 2 freshened types - this works even with the closure encoding.
+//!
+//! __An important detail concerning regions.__ The freshener also replaces *all* free regions with
+//! 'erased. The reason behind this is that, in general, we do not take region relationships into
+//! account when making type-overloaded decisions. This is important because of the design of the
+//! region inferencer, which is not based on unification but rather on accumulating and then
+//! solving a set of constraints. In contrast, the type inferencer assigns a value to each type
+//! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type
+//! inferencer knows "so far".
+
+use rustc_middle::ty::fold::TypeFolder;
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
+
+use rustc_data_structures::fx::FxHashMap;
+
+use std::collections::hash_map::Entry;
+
+use super::unify_key::ToType;
+use super::InferCtxt;
+
+pub struct TypeFreshener<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    ty_freshen_count: u32,
+    const_freshen_count: u32,
+    ty_freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>,
+    const_freshen_map: FxHashMap<ty::InferConst<'tcx>, &'tcx ty::Const<'tcx>>,
+    keep_static: bool,
+}
+
+impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>, keep_static: bool) -> TypeFreshener<'a, 'tcx> {
+        TypeFreshener {
+            infcx,
+            ty_freshen_count: 0,
+            const_freshen_count: 0,
+            ty_freshen_map: Default::default(),
+            const_freshen_map: Default::default(),
+            keep_static,
+        }
+    }
+
+    fn freshen_ty<F>(
+        &mut self,
+        opt_ty: Option<Ty<'tcx>>,
+        key: ty::InferTy,
+        freshener: F,
+    ) -> Ty<'tcx>
+    where
+        F: FnOnce(u32) -> ty::InferTy,
+    {
+        if let Some(ty) = opt_ty {
+            return ty.fold_with(self);
+        }
+
+        match self.ty_freshen_map.entry(key) {
+            Entry::Occupied(entry) => *entry.get(),
+            Entry::Vacant(entry) => {
+                let index = self.ty_freshen_count;
+                self.ty_freshen_count += 1;
+                let t = self.infcx.tcx.mk_ty_infer(freshener(index));
+                entry.insert(t);
+                t
+            }
+        }
+    }
+
+    fn freshen_const<F>(
+        &mut self,
+        opt_ct: Option<&'tcx ty::Const<'tcx>>,
+        key: ty::InferConst<'tcx>,
+        freshener: F,
+        ty: Ty<'tcx>,
+    ) -> &'tcx ty::Const<'tcx>
+    where
+        F: FnOnce(u32) -> ty::InferConst<'tcx>,
+    {
+        if let Some(ct) = opt_ct {
+            return ct.fold_with(self);
+        }
+
+        match self.const_freshen_map.entry(key) {
+            Entry::Occupied(entry) => *entry.get(),
+            Entry::Vacant(entry) => {
+                let index = self.const_freshen_count;
+                self.const_freshen_count += 1;
+                let ct = self.infcx.tcx.mk_const_infer(freshener(index), ty);
+                entry.insert(ct);
+                ct
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'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::ReEarlyBound(..)
+            | ty::ReFree(_)
+            | ty::ReVar(_)
+            | ty::RePlaceholder(..)
+            | ty::ReEmpty(_)
+            | ty::ReErased => {
+                // replace all free regions with 'erased
+                self.tcx().lifetimes.re_erased
+            }
+            ty::ReStatic => {
+                if self.keep_static {
+                    r
+                } else {
+                    self.tcx().lifetimes.re_erased
+                }
+            }
+        }
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        if !t.needs_infer() && !t.has_erasable_regions() {
+            return t;
+        }
+
+        let tcx = self.infcx.tcx;
+
+        match *t.kind() {
+            ty::Infer(ty::TyVar(v)) => {
+                let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
+                self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)
+            }
+
+            ty::Infer(ty::IntVar(v)) => self.freshen_ty(
+                self.infcx
+                    .inner
+                    .borrow_mut()
+                    .int_unification_table()
+                    .probe_value(v)
+                    .map(|v| v.to_type(tcx)),
+                ty::IntVar(v),
+                ty::FreshIntTy,
+            ),
+
+            ty::Infer(ty::FloatVar(v)) => self.freshen_ty(
+                self.infcx
+                    .inner
+                    .borrow_mut()
+                    .float_unification_table()
+                    .probe_value(v)
+                    .map(|v| v.to_type(tcx)),
+                ty::FloatVar(v),
+                ty::FreshFloatTy,
+            ),
+
+            ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => {
+                if ct >= self.ty_freshen_count {
+                    bug!(
+                        "Encountered a freshend type with id {} \
+                          but our counter is only at {}",
+                        ct,
+                        self.ty_freshen_count
+                    );
+                }
+                t
+            }
+
+            ty::Generator(..)
+            | ty::Bool
+            | ty::Char
+            | ty::Int(..)
+            | ty::Uint(..)
+            | ty::Float(..)
+            | ty::Adt(..)
+            | ty::Str
+            | ty::Error(_)
+            | ty::Array(..)
+            | ty::Slice(..)
+            | ty::RawPtr(..)
+            | ty::Ref(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Dynamic(..)
+            | ty::Never
+            | ty::Tuple(..)
+            | ty::Projection(..)
+            | ty::Foreign(..)
+            | ty::Param(..)
+            | ty::Closure(..)
+            | ty::GeneratorWitness(..)
+            | ty::Opaque(..) => t.super_fold_with(self),
+
+            ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
+        }
+    }
+
+    fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        match ct.val {
+            ty::ConstKind::Infer(ty::InferConst::Var(v)) => {
+                let opt_ct = self
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .const_unification_table()
+                    .probe_value(v)
+                    .val
+                    .known();
+                return self.freshen_const(
+                    opt_ct,
+                    ty::InferConst::Var(v),
+                    ty::InferConst::Fresh,
+                    ct.ty,
+                );
+            }
+            ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
+                if i >= self.const_freshen_count {
+                    bug!(
+                        "Encountered a freshend const with id {} \
+                            but our counter is only at {}",
+                        i,
+                        self.const_freshen_count,
+                    );
+                }
+                return ct;
+            }
+
+            ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
+                bug!("unexpected const {:?}", ct)
+            }
+
+            ty::ConstKind::Param(_)
+            | ty::ConstKind::Value(_)
+            | ty::ConstKind::Unevaluated(..)
+            | ty::ConstKind::Error(_) => {}
+        }
+
+        ct.super_fold_with(self)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/fudge.rs b/compiler/rustc_infer/src/infer/fudge.rs
new file mode 100644
index 00000000000..c292b2bdb30
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/fudge.rs
@@ -0,0 +1,249 @@
+use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
+use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid};
+
+use super::type_variable::TypeVariableOrigin;
+use super::InferCtxt;
+use super::{ConstVariableOrigin, RegionVariableOrigin, UnificationTable};
+
+use rustc_data_structures::snapshot_vec as sv;
+use rustc_data_structures::unify as ut;
+use ut::UnifyKey;
+
+use std::ops::Range;
+
+fn vars_since_snapshot<'tcx, T>(
+    table: &mut UnificationTable<'_, 'tcx, T>,
+    snapshot_var_len: usize,
+) -> Range<T>
+where
+    T: UnifyKey,
+    super::UndoLog<'tcx>: From<sv::UndoLog<ut::Delegate<T>>>,
+{
+    T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32)
+}
+
+fn const_vars_since_snapshot<'tcx>(
+    table: &mut UnificationTable<'_, 'tcx, ConstVid<'tcx>>,
+    snapshot_var_len: usize,
+) -> (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>) {
+    let range = vars_since_snapshot(table, snapshot_var_len);
+    (
+        range.start..range.end,
+        (range.start.index..range.end.index)
+            .map(|index| table.probe_value(ConstVid::from_index(index)).origin)
+            .collect(),
+    )
+}
+
+struct VariableLengths {
+    type_var_len: usize,
+    const_var_len: usize,
+    int_var_len: usize,
+    float_var_len: usize,
+    region_constraints_len: usize,
+}
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    fn variable_lengths(&self) -> VariableLengths {
+        let mut inner = self.inner.borrow_mut();
+        VariableLengths {
+            type_var_len: inner.type_variables().num_vars(),
+            const_var_len: inner.const_unification_table().len(),
+            int_var_len: inner.int_unification_table().len(),
+            float_var_len: inner.float_unification_table().len(),
+            region_constraints_len: inner.unwrap_region_constraints().num_region_vars(),
+        }
+    }
+
+    /// This rather funky routine is used while processing expected
+    /// types. What happens here is that we want to propagate a
+    /// coercion through the return type of a fn to its
+    /// argument. Consider the type of `Option::Some`, which is
+    /// basically `for<T> fn(T) -> Option<T>`. So if we have an
+    /// expression `Some(&[1, 2, 3])`, and that has the expected type
+    /// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]`
+    /// with the expectation of `&[u32]`. This will cause us to coerce
+    /// from `&[u32; 3]` to `&[u32]` and make the users life more
+    /// pleasant.
+    ///
+    /// The way we do this is using `fudge_inference_if_ok`. What the
+    /// routine actually does is to start a snapshot and execute the
+    /// closure `f`. In our example above, what this closure will do
+    /// is to unify the expectation (`Option<&[u32]>`) with the actual
+    /// return type (`Option<?T>`, where `?T` represents the variable
+    /// instantiated for `T`). This will cause `?T` to be unified
+    /// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The
+    /// input type (`?T`) is then returned by `f()`.
+    ///
+    /// At this point, `fudge_inference_if_ok` will normalize all type
+    /// variables, converting `?T` to `&?a [u32]` and end the
+    /// snapshot. The problem is that we can't just return this type
+    /// out, because it references the region variable `?a`, and that
+    /// region variable was popped when we popped the snapshot.
+    ///
+    /// So what we do is to keep a list (`region_vars`, in the code below)
+    /// of region variables created during the snapshot (here, `?a`). We
+    /// fold the return value and replace any such regions with a *new*
+    /// region variable (e.g., `?b`) and return the result (`&?b [u32]`).
+    /// This can then be used as the expectation for the fn argument.
+    ///
+    /// The important point here is that, for soundness purposes, the
+    /// regions in question are not particularly important. We will
+    /// use the expected types to guide coercions, but we will still
+    /// type-check the resulting types from those coercions against
+    /// the actual types (`?T`, `Option<?T>`) -- and remember that
+    /// after the snapshot is popped, the variable `?T` is no longer
+    /// unified.
+    pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
+    where
+        F: FnOnce() -> Result<T, E>,
+        T: TypeFoldable<'tcx>,
+    {
+        debug!("fudge_inference_if_ok()");
+
+        let variable_lengths = self.variable_lengths();
+        let (mut fudger, value) = self.probe(|_| {
+            match f() {
+                Ok(value) => {
+                    let value = self.resolve_vars_if_possible(value);
+
+                    // At this point, `value` could in principle refer
+                    // to inference variables that have been created during
+                    // the snapshot. Once we exit `probe()`, those are
+                    // going to be popped, so we will have to
+                    // eliminate any references to them.
+
+                    let mut inner = self.inner.borrow_mut();
+                    let type_vars =
+                        inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len);
+                    let int_vars = vars_since_snapshot(
+                        &mut inner.int_unification_table(),
+                        variable_lengths.int_var_len,
+                    );
+                    let float_vars = vars_since_snapshot(
+                        &mut inner.float_unification_table(),
+                        variable_lengths.float_var_len,
+                    );
+                    let region_vars = inner
+                        .unwrap_region_constraints()
+                        .vars_since_snapshot(variable_lengths.region_constraints_len);
+                    let const_vars = const_vars_since_snapshot(
+                        &mut inner.const_unification_table(),
+                        variable_lengths.const_var_len,
+                    );
+
+                    let fudger = InferenceFudger {
+                        infcx: self,
+                        type_vars,
+                        int_vars,
+                        float_vars,
+                        region_vars,
+                        const_vars,
+                    };
+
+                    Ok((fudger, value))
+                }
+                Err(e) => Err(e),
+            }
+        })?;
+
+        // At this point, we need to replace any of the now-popped
+        // type/region variables that appear in `value` with a fresh
+        // variable of the appropriate kind. We can't do this during
+        // the probe because they would just get popped then too. =)
+
+        // Micro-optimization: if no variables have been created, then
+        // `value` can't refer to any of them. =) So we can just return it.
+        if fudger.type_vars.0.is_empty()
+            && fudger.int_vars.is_empty()
+            && fudger.float_vars.is_empty()
+            && fudger.region_vars.0.is_empty()
+            && fudger.const_vars.0.is_empty()
+        {
+            Ok(value)
+        } else {
+            Ok(value.fold_with(&mut fudger))
+        }
+    }
+}
+
+pub struct InferenceFudger<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>),
+    int_vars: Range<IntVid>,
+    float_vars: Range<FloatVid>,
+    region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>),
+    const_vars: (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>),
+}
+
+impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        match *ty.kind() {
+            ty::Infer(ty::InferTy::TyVar(vid)) => {
+                if self.type_vars.0.contains(&vid) {
+                    // This variable was created during the fudging.
+                    // Recreate it with a fresh variable here.
+                    let idx = (vid.index - self.type_vars.0.start.index) as usize;
+                    let origin = self.type_vars.1[idx];
+                    self.infcx.next_ty_var(origin)
+                } else {
+                    // This variable was created before the
+                    // "fudging". Since we refresh all type
+                    // variables to their binding anyhow, we know
+                    // that it is unbound, so we can just return
+                    // it.
+                    debug_assert!(
+                        self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown()
+                    );
+                    ty
+                }
+            }
+            ty::Infer(ty::InferTy::IntVar(vid)) => {
+                if self.int_vars.contains(&vid) {
+                    self.infcx.next_int_var()
+                } else {
+                    ty
+                }
+            }
+            ty::Infer(ty::InferTy::FloatVar(vid)) => {
+                if self.float_vars.contains(&vid) {
+                    self.infcx.next_float_var()
+                } else {
+                    ty
+                }
+            }
+            _ => ty.super_fold_with(self),
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        if let ty::ReVar(vid) = *r {
+            if self.region_vars.0.contains(&vid) {
+                let idx = vid.index() - self.region_vars.0.start.index();
+                let origin = self.region_vars.1[idx];
+                return self.infcx.next_region_var(origin);
+            }
+        }
+        r
+    }
+
+    fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        if let ty::Const { val: ty::ConstKind::Infer(ty::InferConst::Var(vid)), ty } = ct {
+            if self.const_vars.0.contains(&vid) {
+                // This variable was created during the fudging.
+                // Recreate it with a fresh variable here.
+                let idx = (vid.index - self.const_vars.0.start.index) as usize;
+                let origin = self.const_vars.1[idx];
+                self.infcx.next_const_var(ty, origin)
+            } else {
+                ct
+            }
+        } else {
+            ct.super_fold_with(self)
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs
new file mode 100644
index 00000000000..60f02b84aa3
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/glb.rs
@@ -0,0 +1,126 @@
+use super::combine::CombineFields;
+use super::lattice::{self, LatticeDir};
+use super::InferCtxt;
+use super::Subtype;
+
+use crate::infer::combine::ConstEquateRelation;
+use crate::traits::ObligationCause;
+use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+/// "Greatest lower bound" (common subtype)
+pub struct Glb<'combine, 'infcx, 'tcx> {
+    fields: &'combine mut CombineFields<'infcx, 'tcx>,
+    a_is_expected: bool,
+}
+
+impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> {
+    pub fn new(
+        fields: &'combine mut CombineFields<'infcx, 'tcx>,
+        a_is_expected: bool,
+    ) -> Glb<'combine, 'infcx, 'tcx> {
+        Glb { fields, a_is_expected }
+    }
+}
+
+impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> {
+    fn tag(&self) -> &'static str {
+        "Glb"
+    }
+
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.fields.tcx()
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.fields.param_env
+    }
+
+    fn a_is_expected(&self) -> bool {
+        self.a_is_expected
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        match variance {
+            ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
+            ty::Covariant => self.relate(a, b),
+            // FIXME(#41044) -- not correct, need test
+            ty::Bivariant => Ok(a),
+            ty::Contravariant => self.fields.lub(self.a_is_expected).relate(a, b),
+        }
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        lattice::super_lattice_tys(self, a, b)
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
+
+        let origin = Subtype(box self.fields.trace.clone());
+        Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions(
+            self.tcx(),
+            origin,
+            a,
+            b,
+        ))
+    }
+
+    fn consts(
+        &mut self,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        self.fields.infcx.super_combine_consts(self, a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        debug!("binders(a={:?}, b={:?})", a, b);
+
+        // When higher-ranked types are involved, computing the LUB is
+        // very challenging, switch to invariance. This is obviously
+        // overly conservative but works ok in practice.
+        self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
+        Ok(a)
+    }
+}
+
+impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, 'tcx> {
+    fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> {
+        self.fields.infcx
+    }
+
+    fn cause(&self) -> &ObligationCause<'tcx> {
+        &self.fields.trace.cause
+    }
+
+    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);
+        sub.relate(v, a)?;
+        sub.relate(v, b)?;
+        Ok(())
+    }
+}
+
+impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> {
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/higher_ranked/README.md b/compiler/rustc_infer/src/infer/higher_ranked/README.md
new file mode 100644
index 00000000000..533d0ef7e6c
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/higher_ranked/README.md
@@ -0,0 +1,8 @@
+To learn more about how Higher-ranked trait bounds work in the _old_ trait
+solver, see [this chapter][oldhrtb] of the rustc-dev-guide.
+
+To learn more about how they work in the _new_ trait solver, see [this
+chapter][newhrtb].
+
+[oldhrtb]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
+[newhrtb]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference.html#placeholders-and-universes
diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
new file mode 100644
index 00000000000..d460222df8a
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
@@ -0,0 +1,150 @@
+//! Helper routines for higher-ranked things. See the `doc` module at
+//! the end of the file for details.
+
+use super::combine::CombineFields;
+use super::{HigherRankedType, InferCtxt};
+
+use crate::infer::CombinedSnapshot;
+use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::{self, Binder, TypeFoldable};
+
+impl<'a, 'tcx> CombineFields<'a, 'tcx> {
+    pub fn higher_ranked_sub<T>(
+        &mut self,
+        a: Binder<'tcx, T>,
+        b: Binder<'tcx, T>,
+        a_is_expected: bool,
+    ) -> RelateResult<'tcx, Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        debug!("higher_ranked_sub(a={:?}, b={:?})", a, b);
+
+        // Rather than checking the subtype relationship between `a` and `b`
+        // as-is, we need to do some extra work here in order to make sure
+        // that function subtyping works correctly with respect to regions
+        //
+        // Note: this is a subtle algorithm.  For a full explanation, please see
+        // the rustc dev guide:
+        // <https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html>
+
+        let span = self.trace.cause.span;
+
+        self.infcx.commit_if_ok(|_| {
+            // First, we instantiate each bound region in the supertype with a
+            // fresh placeholder region.
+            let b_prime = self.infcx.replace_bound_vars_with_placeholders(b);
+
+            // Next, we instantiate each bound region in the subtype
+            // with a fresh region variable. These region variables --
+            // but no other pre-existing region variables -- can name
+            // the placeholders.
+            let (a_prime, _) =
+                self.infcx.replace_bound_vars_with_fresh_vars(span, HigherRankedType, a);
+
+            debug!("a_prime={:?}", a_prime);
+            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)?;
+
+            debug!("higher_ranked_sub: OK result={:?}", result);
+
+            // We related `a_prime` and `b_prime`, which just had any bound vars
+            // replaced with placeholders or infer vars, respectively. Relating
+            // them should not introduce new bound vars.
+            Ok(ty::Binder::dummy(result))
+        })
+    }
+}
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    /// Replaces all regions (resp. types) bound by `binder` with placeholder
+    /// regions (resp. types) and return a map indicating which bound-region
+    /// placeholder region. This is the first step of checking subtyping
+    /// when higher-ranked things are involved.
+    ///
+    /// **Important:** You have to be careful to not leak these placeholders,
+    /// for more information about how placeholders and HRTBs work, see
+    /// the [rustc dev guide].
+    ///
+    /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
+    pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        // Figure out what the next universe will be, but don't actually create
+        // it until after we've done the substitution (in particular there may
+        // be no bound variables). This is a performance optimization, since the
+        // leak check for example can be skipped if no new universes are created
+        // (i.e., if there are no placeholders).
+        let next_universe = self.universe().next_universe();
+
+        let fld_r = |br: ty::BoundRegion| {
+            self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion {
+                universe: next_universe,
+                name: br.kind,
+            }))
+        };
+
+        let fld_t = |bound_ty: ty::BoundTy| {
+            self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
+                universe: next_universe,
+                name: bound_ty.var,
+            }))
+        };
+
+        let fld_c = |bound_var: ty::BoundVar, ty| {
+            self.tcx.mk_const(ty::Const {
+                val: ty::ConstKind::Placeholder(ty::PlaceholderConst {
+                    universe: next_universe,
+                    name: ty::BoundConst { var: bound_var, ty },
+                }),
+                ty,
+            })
+        };
+
+        let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t, fld_c);
+
+        // If there were higher-ranked regions to replace, then actually create
+        // the next universe (this avoids needlessly creating universes).
+        if !map.is_empty() {
+            let n_u = self.create_next_universe();
+            assert_eq!(n_u, next_universe);
+        }
+
+        debug!(
+            "replace_bound_vars_with_placeholders(\
+             next_universe={:?}, \
+             result={:?}, \
+             map={:?})",
+            next_universe, result, map,
+        );
+
+        result
+    }
+
+    /// See `infer::region_constraints::RegionConstraintCollector::leak_check`.
+    pub fn leak_check(
+        &self,
+        overly_polymorphic: bool,
+        snapshot: &CombinedSnapshot<'_, 'tcx>,
+    ) -> RelateResult<'tcx, ()> {
+        // If the user gave `-Zno-leak-check`, or we have been
+        // configured to skip the leak check, then skip the leak check
+        // completely. The leak check is deprecated. Any legitimate
+        // subtyping errors that it would have caught will now be
+        // caught later on, during region checking. However, we
+        // continue to use it for a transition period.
+        if self.tcx.sess.opts.debugging_opts.no_leak_check || self.skip_leak_check.get() {
+            return Ok(());
+        }
+
+        self.inner.borrow_mut().unwrap_region_constraints().leak_check(
+            self.tcx,
+            overly_polymorphic,
+            self.universe(),
+            snapshot,
+        )
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs
new file mode 100644
index 00000000000..c47d4769637
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/lattice.rs
@@ -0,0 +1,99 @@
+//! # Lattice Variables
+//!
+//! This file contains generic code for operating on inference variables
+//! that are characterized by an upper- and lower-bound. The logic and
+//! reasoning is explained in detail in the large comment in `infer.rs`.
+//!
+//! The code in here is defined quite generically so that it can be
+//! applied both to type variables, which represent types being inferred,
+//! and fn variables, which represent function types being inferred.
+//! It may eventually be applied to their types as well, who knows.
+//! In some cases, the functions are also generic with respect to the
+//! operation on the lattice (GLB vs LUB).
+//!
+//! Although all the functions are generic, we generally write the
+//! comments in a way that is specific to type variables and the LUB
+//! operation. It's just easier that way.
+//!
+//! In general all of the functions are defined parametrically
+//! over a `LatticeValue`, which is a value defined with respect to
+//! a lattice.
+
+use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use super::InferCtxt;
+
+use crate::traits::ObligationCause;
+use rustc_middle::ty::relate::{RelateResult, TypeRelation};
+use rustc_middle::ty::TyVar;
+use rustc_middle::ty::{self, Ty};
+
+pub trait LatticeDir<'f, 'tcx>: TypeRelation<'tcx> {
+    fn infcx(&self) -> &'f InferCtxt<'f, 'tcx>;
+
+    fn cause(&self) -> &ObligationCause<'tcx>;
+
+    // Relates the type `v` to `a` and `b` such that `v` represents
+    // the LUB/GLB of `a` and `b` as appropriate.
+    //
+    // Subtle hack: ordering *may* be significant here. This method
+    // relates `v` to `a` first, which may help us to avoid unnecessary
+    // type variable obligations. See caller for details.
+    fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>;
+}
+
+pub fn super_lattice_tys<'a, 'tcx: 'a, L>(
+    this: &mut L,
+    a: Ty<'tcx>,
+    b: Ty<'tcx>,
+) -> RelateResult<'tcx, Ty<'tcx>>
+where
+    L: LatticeDir<'a, 'tcx>,
+{
+    debug!("{}.lattice_tys({:?}, {:?})", this.tag(), a, b);
+
+    if a == b {
+        return Ok(a);
+    }
+
+    let infcx = this.infcx();
+    let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
+    let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
+    match (a.kind(), b.kind()) {
+        // 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
+        // first to `relate_bound`). Otherwise, we would produce a
+        // subtype obligation that must then be processed.
+        //
+        // Example: if the LHS is a type variable, and RHS is
+        // `Box<i32>`, then we current compare `v` to the RHS first,
+        // which will instantiate `v` with `Box<i32>`.  Then when `v`
+        // is compared to the LHS, we instantiate LHS with `Box<i32>`.
+        // But if we did in reverse order, we would create a `v <:
+        // LHS` (or vice versa) constraint and then instantiate
+        // `v`. This would require further processing to achieve same
+        // end-result; in partiular, this screws up some of the logic
+        // in coercion, which expects LUB to figure out that the LHS
+        // 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::Infer(TyVar(..)), _) => {
+            let v = infcx.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::LatticeVariable,
+                span: this.cause().span,
+            });
+            this.relate_bound(v, b, a)?;
+            Ok(v)
+        }
+        (_, &ty::Infer(TyVar(..))) => {
+            let v = infcx.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::LatticeVariable,
+                span: this.cause().span,
+            });
+            this.relate_bound(v, a, b)?;
+            Ok(v)
+        }
+
+        _ => infcx.super_combine_tys(this, a, b),
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/README.md b/compiler/rustc_infer/src/infer/lexical_region_resolve/README.md
new file mode 100644
index 00000000000..0a7da8c8063
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/README.md
@@ -0,0 +1,6 @@
+Lexical Region Resolution was removed in https://github.com/rust-lang/rust/pull/64790.
+
+Rust now uses Non-lexical lifetimes. For more info, please see the [borrowck
+chapter][bc] in the rustc-dev-guide.
+
+[bc]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference.html
diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
new file mode 100644
index 00000000000..869fd225d51
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
@@ -0,0 +1,1020 @@
+//! Lexical region resolution.
+
+use crate::infer::region_constraints::Constraint;
+use crate::infer::region_constraints::GenericKind;
+use crate::infer::region_constraints::MemberConstraint;
+use crate::infer::region_constraints::RegionConstraintData;
+use crate::infer::region_constraints::VarInfos;
+use crate::infer::region_constraints::VerifyBound;
+use crate::infer::RegionRelations;
+use crate::infer::RegionVariableOrigin;
+use crate::infer::RegionckMode;
+use crate::infer::SubregionOrigin;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::graph::implementation::{
+    Direction, Graph, NodeIndex, INCOMING, OUTGOING,
+};
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic};
+use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar};
+use rustc_middle::ty::{Region, RegionVid};
+use rustc_span::Span;
+use std::fmt;
+
+/// This function performs lexical region resolution given a complete
+/// set of constraints and variable origins. It performs a fixed-point
+/// iteration to find region values which satisfy all constraints,
+/// assuming such values can be found. It returns the final values of
+/// all the variables as well as a set of errors that must be reported.
+pub fn resolve<'tcx>(
+    region_rels: &RegionRelations<'_, 'tcx>,
+    var_infos: VarInfos,
+    data: RegionConstraintData<'tcx>,
+    mode: RegionckMode,
+) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) {
+    debug!("RegionConstraintData: resolve_regions()");
+    let mut errors = vec![];
+    let mut resolver = LexicalResolver { region_rels, var_infos, data };
+    match mode {
+        RegionckMode::Solve => {
+            let values = resolver.infer_variable_values(&mut errors);
+            (values, errors)
+        }
+        RegionckMode::Erase { suppress_errors: false } => {
+            // Do real inference to get errors, then erase the results.
+            let mut values = resolver.infer_variable_values(&mut errors);
+            let re_erased = region_rels.tcx.lifetimes.re_erased;
+
+            values.values.iter_mut().for_each(|v| match *v {
+                VarValue::Value(ref mut r) => *r = re_erased,
+                VarValue::ErrorValue => {}
+            });
+            (values, errors)
+        }
+        RegionckMode::Erase { suppress_errors: true } => {
+            // Skip region inference entirely.
+            (resolver.erased_data(region_rels.tcx), Vec::new())
+        }
+    }
+}
+
+/// Contains the result of lexical region resolution. Offers methods
+/// to lookup up the final value of a region variable.
+pub struct LexicalRegionResolutions<'tcx> {
+    values: IndexVec<RegionVid, VarValue<'tcx>>,
+    error_region: ty::Region<'tcx>,
+}
+
+#[derive(Copy, Clone, Debug)]
+enum VarValue<'tcx> {
+    Value(Region<'tcx>),
+    ErrorValue,
+}
+
+#[derive(Clone, Debug)]
+pub enum RegionResolutionError<'tcx> {
+    /// `ConcreteFailure(o, a, b)`:
+    ///
+    /// `o` requires that `a <= b`, but this does not hold
+    ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>),
+
+    /// `GenericBoundFailure(p, s, a)
+    ///
+    /// The parameter/associated-type `p` must be known to outlive the lifetime
+    /// `a` (but none of the known bounds are sufficient).
+    GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>),
+
+    /// `SubSupConflict(v, v_origin, sub_origin, sub_r, sup_origin, sup_r)`:
+    ///
+    /// Could not infer a value for `v` (which has origin `v_origin`)
+    /// because `sub_r <= v` (due to `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and
+    /// `sub_r <= sup_r` does not hold.
+    SubSupConflict(
+        RegionVid,
+        RegionVariableOrigin,
+        SubregionOrigin<'tcx>,
+        Region<'tcx>,
+        SubregionOrigin<'tcx>,
+        Region<'tcx>,
+    ),
+
+    /// Indicates a `'b: 'a` constraint where `'a` is in a universe that
+    /// cannot name the placeholder `'b`.
+    UpperBoundUniverseConflict(
+        RegionVid,
+        RegionVariableOrigin,
+        ty::UniverseIndex,     // the universe index of the region variable
+        SubregionOrigin<'tcx>, // cause of the constraint
+        Region<'tcx>,          // the placeholder `'b`
+    ),
+
+    /// Indicates a failure of a `MemberConstraint`. These arise during
+    /// impl trait processing explicitly -- basically, the impl trait's hidden type
+    /// included some region that it was not supposed to.
+    MemberConstraintFailure { span: Span, hidden_ty: Ty<'tcx>, member_region: Region<'tcx> },
+}
+
+struct RegionAndOrigin<'tcx> {
+    region: Region<'tcx>,
+    origin: SubregionOrigin<'tcx>,
+}
+
+type RegionGraph<'tcx> = Graph<(), Constraint<'tcx>>;
+
+struct LexicalResolver<'cx, 'tcx> {
+    region_rels: &'cx RegionRelations<'cx, 'tcx>,
+    var_infos: VarInfos,
+    data: RegionConstraintData<'tcx>,
+}
+
+impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.region_rels.tcx
+    }
+
+    fn infer_variable_values(
+        &mut self,
+        errors: &mut Vec<RegionResolutionError<'tcx>>,
+    ) -> LexicalRegionResolutions<'tcx> {
+        let mut var_data = self.construct_var_data(self.tcx());
+
+        // Dorky hack to cause `dump_constraints` to only get called
+        // if debug mode is enabled:
+        debug!(
+            "----() End constraint listing (context={:?}) {:?}---",
+            self.region_rels.context,
+            self.dump_constraints(self.region_rels)
+        );
+
+        let graph = self.construct_graph();
+        self.expand_givens(&graph);
+        loop {
+            self.expansion(&mut var_data);
+            if !self.enforce_member_constraints(&graph, &mut var_data) {
+                break;
+            }
+        }
+        self.collect_errors(&mut var_data, errors);
+        self.collect_var_errors(&var_data, &graph, errors);
+        var_data
+    }
+
+    fn num_vars(&self) -> usize {
+        self.var_infos.len()
+    }
+
+    /// Initially, the value for all variables is set to `'empty`, the
+    /// empty region. The `expansion` phase will grow this larger.
+    fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> {
+        LexicalRegionResolutions {
+            error_region: tcx.lifetimes.re_static,
+            values: IndexVec::from_fn_n(
+                |vid| {
+                    let vid_universe = self.var_infos[vid].universe;
+                    let re_empty = tcx.mk_region(ty::ReEmpty(vid_universe));
+                    VarValue::Value(re_empty)
+                },
+                self.num_vars(),
+            ),
+        }
+    }
+
+    /// An erased version of the lexical region resolutions. Used when we're
+    /// erasing regions and suppressing errors: in item bodies with
+    /// `-Zborrowck=mir`.
+    fn erased_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> {
+        LexicalRegionResolutions {
+            error_region: tcx.lifetimes.re_static,
+            values: IndexVec::from_elem_n(
+                VarValue::Value(tcx.lifetimes.re_erased),
+                self.num_vars(),
+            ),
+        }
+    }
+
+    fn dump_constraints(&self, free_regions: &RegionRelations<'_, 'tcx>) {
+        debug!("----() Start constraint listing (context={:?}) ()----", free_regions.context);
+        for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() {
+            debug!("Constraint {} => {:?}", idx, constraint);
+        }
+    }
+
+    fn expand_givens(&mut self, graph: &RegionGraph<'_>) {
+        // Givens are a kind of horrible hack to account for
+        // constraints like 'c <= '0 that are known to hold due to
+        // closure signatures (see the comment above on the `givens`
+        // field). They should go away. But until they do, the role
+        // of this fn is to account for the transitive nature:
+        //
+        //     Given 'c <= '0
+        //     and   '0 <= '1
+        //     then  'c <= '1
+
+        let seeds: Vec<_> = self.data.givens.iter().cloned().collect();
+        for (r, vid) in seeds {
+            // While all things transitively reachable in the graph
+            // from the variable (`'0` in the example above).
+            let seed_index = NodeIndex(vid.index() as usize);
+            for succ_index in graph.depth_traverse(seed_index, OUTGOING) {
+                let succ_index = succ_index.0;
+
+                // The first N nodes correspond to the region
+                // variables. Other nodes correspond to constant
+                // regions.
+                if succ_index < self.num_vars() {
+                    let succ_vid = RegionVid::new(succ_index);
+
+                    // Add `'c <= '1`.
+                    self.data.givens.insert((r, succ_vid));
+                }
+            }
+        }
+    }
+
+    /// Enforce all member constraints and return true if anything
+    /// changed. See `enforce_member_constraint` for more details.
+    fn enforce_member_constraints(
+        &self,
+        graph: &RegionGraph<'tcx>,
+        var_values: &mut LexicalRegionResolutions<'tcx>,
+    ) -> bool {
+        // Note: we don't use the `any` combinator because we don't
+        // want to stop at the first constraint that makes a change.
+        let mut any_changed = false;
+        for member_constraint in &self.data.member_constraints {
+            any_changed |= self.enforce_member_constraint(graph, member_constraint, var_values);
+        }
+        any_changed
+    }
+
+    /// Enforce a constraint like
+    ///
+    /// ```
+    /// 'r member of ['c...]
+    /// ```
+    ///
+    /// We look for all choice regions from the list `'c...` that:
+    ///
+    /// (a) are greater than the current value of `'r` (which is a lower bound)
+    ///
+    /// and
+    ///
+    /// (b) are compatible with the upper bounds of `'r` that we can
+    /// find by traversing the graph.
+    ///
+    /// From that list, we look for a *minimal* option `'c_min`. If we
+    /// find one, then we can enforce that `'r: 'c_min`.
+    fn enforce_member_constraint(
+        &self,
+        graph: &RegionGraph<'tcx>,
+        member_constraint: &MemberConstraint<'tcx>,
+        var_values: &mut LexicalRegionResolutions<'tcx>,
+    ) -> bool {
+        debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint);
+
+        // The constraint is some inference variable (`vid`) which
+        // must be equal to one of the options.
+        let member_vid = match member_constraint.member_region {
+            ty::ReVar(vid) => *vid,
+            _ => return false,
+        };
+
+        // The current value of `vid` is a lower bound LB -- i.e., we
+        // know that `LB <= vid` must be true.
+        let member_lower_bound: ty::Region<'tcx> = match var_values.value(member_vid) {
+            VarValue::ErrorValue => return false,
+            VarValue::Value(r) => r,
+        };
+
+        // Find all the "upper bounds" -- that is, each region `b` such that
+        // `r0 <= b` must hold.
+        let (member_upper_bounds, ..) =
+            self.collect_bounding_regions(graph, member_vid, OUTGOING, None);
+
+        // Get an iterator over the *available choice* -- that is,
+        // each choice region `c` where `lb <= c` and `c <= ub` for all the
+        // upper bounds `ub`.
+        debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds);
+        let mut options = member_constraint.choice_regions.iter().filter(|option| {
+            self.sub_concrete_regions(member_lower_bound, option)
+                && member_upper_bounds
+                    .iter()
+                    .all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region))
+        });
+
+        // If there is more than one option, we only make a choice if
+        // there is a single *least* choice -- i.e., some available
+        // region that is `<=` all the others.
+        let mut least_choice: ty::Region<'tcx> = match options.next() {
+            Some(&r) => r,
+            None => return false,
+        };
+        debug!("enforce_member_constraint: least_choice={:?}", least_choice);
+        for &option in options {
+            debug!("enforce_member_constraint: option={:?}", option);
+            if !self.sub_concrete_regions(least_choice, option) {
+                if self.sub_concrete_regions(option, least_choice) {
+                    debug!("enforce_member_constraint: new least choice");
+                    least_choice = option;
+                } else {
+                    debug!("enforce_member_constraint: no least choice");
+                    return false;
+                }
+            }
+        }
+
+        // (#72087) Different `ty::Regions` can be known to be equal, for
+        // example, we know that `'a` and `'static` are equal in a function
+        // with a parameter of type `&'static &'a ()`.
+        //
+        // When we have two equal regions like this `expansion` will use
+        // `lub_concrete_regions` to pick a canonical representative. The same
+        // choice is needed here so that we don't end up in a cycle of
+        // `expansion` changing the region one way and the code here changing
+        // it back.
+        let lub = self.lub_concrete_regions(least_choice, member_lower_bound);
+        debug!(
+            "enforce_member_constraint: final least choice = {:?}\nlub = {:?}",
+            least_choice, lub
+        );
+        if lub != member_lower_bound {
+            *var_values.value_mut(member_vid) = VarValue::Value(least_choice);
+            true
+        } else {
+            false
+        }
+    }
+
+    fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) {
+        let mut constraints = IndexVec::from_elem_n(Vec::new(), var_values.values.len());
+        let mut changes = Vec::new();
+        for constraint in self.data.constraints.keys() {
+            let (a_vid, a_region, b_vid, b_data) = match *constraint {
+                Constraint::RegSubVar(a_region, b_vid) => {
+                    let b_data = var_values.value_mut(b_vid);
+                    (None, a_region, b_vid, b_data)
+                }
+                Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) {
+                    VarValue::ErrorValue => continue,
+                    VarValue::Value(a_region) => {
+                        let b_data = var_values.value_mut(b_vid);
+                        (Some(a_vid), a_region, b_vid, b_data)
+                    }
+                },
+                Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => {
+                    // These constraints are checked after expansion
+                    // is done, in `collect_errors`.
+                    continue;
+                }
+            };
+            if self.expand_node(a_region, b_vid, b_data) {
+                changes.push(b_vid);
+            }
+            if let Some(a_vid) = a_vid {
+                match *b_data {
+                    VarValue::Value(ReStatic) | VarValue::ErrorValue => (),
+                    _ => {
+                        constraints[a_vid].push((a_vid, b_vid));
+                        constraints[b_vid].push((a_vid, b_vid));
+                    }
+                }
+            }
+        }
+
+        while let Some(vid) = changes.pop() {
+            constraints[vid].retain(|&(a_vid, b_vid)| {
+                let a_region = match *var_values.value(a_vid) {
+                    VarValue::ErrorValue => return false,
+                    VarValue::Value(a_region) => a_region,
+                };
+                let b_data = var_values.value_mut(b_vid);
+                if self.expand_node(a_region, b_vid, b_data) {
+                    changes.push(b_vid);
+                }
+                !matches!(b_data, VarValue::Value(ReStatic) | VarValue::ErrorValue)
+            });
+        }
+    }
+
+    fn expand_node(
+        &self,
+        a_region: Region<'tcx>,
+        b_vid: RegionVid,
+        b_data: &mut VarValue<'tcx>,
+    ) -> bool {
+        debug!("expand_node({:?}, {:?} == {:?})", a_region, b_vid, b_data);
+
+        match *a_region {
+            // Check if this relationship is implied by a given.
+            ty::ReEarlyBound(_) | ty::ReFree(_) => {
+                if self.data.givens.contains(&(a_region, b_vid)) {
+                    debug!("given");
+                    return false;
+                }
+            }
+
+            _ => {}
+        }
+
+        match *b_data {
+            VarValue::Value(cur_region) => {
+                // This is a specialized version of the `lub_concrete_regions`
+                // check below for a common case, here purely as an
+                // optimization.
+                let b_universe = self.var_infos[b_vid].universe;
+                if let ReEmpty(a_universe) = a_region {
+                    if *a_universe == b_universe {
+                        return false;
+                    }
+                }
+
+                let mut lub = self.lub_concrete_regions(a_region, cur_region);
+                if lub == cur_region {
+                    return false;
+                }
+
+                // Watch out for `'b: !1` relationships, where the
+                // universe of `'b` can't name the placeholder `!1`. In
+                // that case, we have to grow `'b` to be `'static` for the
+                // relationship to hold. 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`.
+                //
+                // (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.)
+                if let ty::RePlaceholder(p) = lub {
+                    if b_universe.cannot_name(p.universe) {
+                        lub = self.tcx().lifetimes.re_static;
+                    }
+                }
+
+                debug!("Expanding value of {:?} from {:?} to {:?}", b_vid, cur_region, lub);
+
+                *b_data = VarValue::Value(lub);
+                true
+            }
+
+            VarValue::ErrorValue => false,
+        }
+    }
+
+    /// True if `a <= b`, but not defined over inference variables.
+    fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool {
+        let tcx = self.tcx();
+        let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2);
+
+        // Check for the case where we know that `'b: 'static` -- in that case,
+        // `a <= b` for all `a`.
+        let b_free_or_static = self.region_rels.free_regions.is_free_or_static(b);
+        if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) {
+            return true;
+        }
+
+        // If both `a` and `b` are free, consult the declared
+        // relationships.  Note that this can be more precise than the
+        // `lub` relationship defined below, since sometimes the "lub"
+        // is actually the `postdom_upper_bound` (see
+        // `TransitiveRelation` for more details).
+        let a_free_or_static = self.region_rels.free_regions.is_free_or_static(a);
+        if a_free_or_static && b_free_or_static {
+            return sub_free_regions(a, b);
+        }
+
+        // For other cases, leverage the LUB code to find the LUB and
+        // check if it is equal to `b`.
+        self.lub_concrete_regions(a, b) == b
+    }
+
+    /// Returns the least-upper-bound of `a` and `b`; i.e., the
+    /// smallest region `c` such that `a <= c` and `b <= c`.
+    ///
+    /// Neither `a` nor `b` may be an inference variable (hence the
+    /// term "concrete regions").
+    fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
+        let r = match (a, b) {
+            (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => {
+                bug!("cannot relate region: LUB({:?}, {:?})", a, b);
+            }
+
+            (&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
+                span_bug!(
+                    self.var_infos[v_id].origin.span(),
+                    "lub_concrete_regions invoked with non-concrete \
+                     regions: {:?}, {:?}",
+                    a,
+                    b
+                );
+            }
+
+            (&ReStatic, _) | (_, &ReStatic) => {
+                // nothing lives longer than `'static`
+                self.tcx().lifetimes.re_static
+            }
+
+            (&ReEmpty(_), r @ (ReEarlyBound(_) | ReFree(_)))
+            | (r @ (ReEarlyBound(_) | ReFree(_)), &ReEmpty(_)) => {
+                // All empty regions are less than early-bound, free,
+                // and scope regions.
+                r
+            }
+
+            (&ReEmpty(a_ui), &ReEmpty(b_ui)) => {
+                // Empty regions are ordered according to the universe
+                // they are associated with.
+                let ui = a_ui.min(b_ui);
+                self.tcx().mk_region(ReEmpty(ui))
+            }
+
+            (&ReEmpty(empty_ui), &RePlaceholder(placeholder))
+            | (&RePlaceholder(placeholder), &ReEmpty(empty_ui)) => {
+                // If this empty region is from a universe that can
+                // name the placeholder, then the placeholder is
+                // larger; otherwise, the only ancestor is `'static`.
+                if empty_ui.can_name(placeholder.universe) {
+                    self.tcx().mk_region(RePlaceholder(placeholder))
+                } else {
+                    self.tcx().lifetimes.re_static
+                }
+            }
+
+            (&ReEarlyBound(_) | &ReFree(_), &ReEarlyBound(_) | &ReFree(_)) => {
+                self.region_rels.lub_free_regions(a, b)
+            }
+
+            // For these types, we cannot define any additional
+            // relationship:
+            (&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => {
+                if a == b {
+                    a
+                } else {
+                    self.tcx().lifetimes.re_static
+                }
+            }
+        };
+
+        debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);
+
+        r
+    }
+
+    /// After expansion is complete, go and check upper bounds (i.e.,
+    /// cases where the region cannot grow larger than a fixed point)
+    /// and check that they are satisfied.
+    fn collect_errors(
+        &self,
+        var_data: &mut LexicalRegionResolutions<'tcx>,
+        errors: &mut Vec<RegionResolutionError<'tcx>>,
+    ) {
+        for (constraint, origin) in &self.data.constraints {
+            debug!("collect_errors: constraint={:?} origin={:?}", constraint, origin);
+            match *constraint {
+                Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => {
+                    // Expansion will ensure that these constraints hold. Ignore.
+                }
+
+                Constraint::RegSubReg(sub, sup) => {
+                    if self.sub_concrete_regions(sub, sup) {
+                        continue;
+                    }
+
+                    debug!(
+                        "collect_errors: region error at {:?}: \
+                         cannot verify that {:?} <= {:?}",
+                        origin, sub, sup
+                    );
+
+                    errors.push(RegionResolutionError::ConcreteFailure(
+                        (*origin).clone(),
+                        sub,
+                        sup,
+                    ));
+                }
+
+                Constraint::VarSubReg(a_vid, b_region) => {
+                    let a_data = var_data.value_mut(a_vid);
+                    debug!("contraction: {:?} == {:?}, {:?}", a_vid, a_data, b_region);
+
+                    let a_region = match *a_data {
+                        VarValue::ErrorValue => continue,
+                        VarValue::Value(a_region) => a_region,
+                    };
+
+                    // Do not report these errors immediately:
+                    // instead, set the variable value to error and
+                    // collect them later.
+                    if !self.sub_concrete_regions(a_region, b_region) {
+                        debug!(
+                            "collect_errors: region error at {:?}: \
+                            cannot verify that {:?}={:?} <= {:?}",
+                            origin, a_vid, a_region, b_region
+                        );
+                        *a_data = VarValue::ErrorValue;
+                    }
+                }
+            }
+        }
+
+        // Check that all member constraints are satisfied.
+        for member_constraint in &self.data.member_constraints {
+            let member_region = var_data.normalize(self.tcx(), member_constraint.member_region);
+            let choice_regions = member_constraint
+                .choice_regions
+                .iter()
+                .map(|&choice_region| var_data.normalize(self.tcx(), choice_region));
+            if !choice_regions.clone().any(|choice_region| member_region == choice_region) {
+                let span = self.tcx().def_span(member_constraint.opaque_type_def_id);
+                errors.push(RegionResolutionError::MemberConstraintFailure {
+                    span,
+                    hidden_ty: member_constraint.hidden_ty,
+                    member_region,
+                });
+            }
+        }
+
+        for verify in &self.data.verifys {
+            debug!("collect_errors: verify={:?}", verify);
+            let sub = var_data.normalize(self.tcx(), verify.region);
+
+            let verify_kind_ty = verify.kind.to_ty(self.tcx());
+            let verify_kind_ty = var_data.normalize(self.tcx(), verify_kind_ty);
+            if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) {
+                continue;
+            }
+
+            debug!(
+                "collect_errors: region error at {:?}: \
+                 cannot verify that {:?} <= {:?}",
+                verify.origin, verify.region, verify.bound
+            );
+
+            errors.push(RegionResolutionError::GenericBoundFailure(
+                verify.origin.clone(),
+                verify.kind,
+                sub,
+            ));
+        }
+    }
+
+    /// Go over the variables that were declared to be error variables
+    /// and create a `RegionResolutionError` for each of them.
+    fn collect_var_errors(
+        &self,
+        var_data: &LexicalRegionResolutions<'tcx>,
+        graph: &RegionGraph<'tcx>,
+        errors: &mut Vec<RegionResolutionError<'tcx>>,
+    ) {
+        debug!("collect_var_errors, var_data = {:#?}", var_data.values);
+
+        // This is the best way that I have found to suppress
+        // duplicate and related errors. Basically we keep a set of
+        // flags for every node. Whenever an error occurs, we will
+        // walk some portion of the graph looking to find pairs of
+        // conflicting regions to report to the user. As we walk, we
+        // trip the flags from false to true, and if we find that
+        // we've already reported an error involving any particular
+        // node we just stop and don't report the current error.  The
+        // idea is to report errors that derive from independent
+        // regions of the graph, but not those that derive from
+        // overlapping locations.
+        let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars());
+
+        for (node_vid, value) in var_data.values.iter_enumerated() {
+            match *value {
+                VarValue::Value(_) => { /* Inference successful */ }
+                VarValue::ErrorValue => {
+                    // Inference impossible: this value contains
+                    // inconsistent constraints.
+                    //
+                    // I think that in this case we should report an
+                    // error now -- unlike the case above, we can't
+                    // wait to see whether the user needs the result
+                    // of this variable. The reason is that the mere
+                    // existence of this variable implies that the
+                    // region graph is inconsistent, whether or not it
+                    // is used.
+                    //
+                    // For example, we may have created a region
+                    // variable that is the GLB of two other regions
+                    // which do not have a GLB. Even if that variable
+                    // is not used, it implies that those two regions
+                    // *should* have a GLB.
+                    //
+                    // At least I think this is true. It may be that
+                    // the mere existence of a conflict in a region
+                    // variable that is not used is not a problem, so
+                    // if this rule starts to create problems we'll
+                    // have to revisit this portion of the code and
+                    // think hard about it. =) -- nikomatsakis
+                    self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors);
+                }
+            }
+        }
+    }
+
+    fn construct_graph(&self) -> RegionGraph<'tcx> {
+        let num_vars = self.num_vars();
+
+        let mut graph = Graph::new();
+
+        for _ in 0..num_vars {
+            graph.add_node(());
+        }
+
+        // Issue #30438: two distinct dummy nodes, one for incoming
+        // edges (dummy_source) and another for outgoing edges
+        // (dummy_sink). In `dummy -> a -> b -> dummy`, using one
+        // dummy node leads one to think (erroneously) there exists a
+        // path from `b` to `a`. Two dummy nodes sidesteps the issue.
+        let dummy_source = graph.add_node(());
+        let dummy_sink = graph.add_node(());
+
+        for constraint in self.data.constraints.keys() {
+            match *constraint {
+                Constraint::VarSubVar(a_id, b_id) => {
+                    graph.add_edge(
+                        NodeIndex(a_id.index() as usize),
+                        NodeIndex(b_id.index() as usize),
+                        *constraint,
+                    );
+                }
+                Constraint::RegSubVar(_, b_id) => {
+                    graph.add_edge(dummy_source, NodeIndex(b_id.index() as usize), *constraint);
+                }
+                Constraint::VarSubReg(a_id, _) => {
+                    graph.add_edge(NodeIndex(a_id.index() as usize), dummy_sink, *constraint);
+                }
+                Constraint::RegSubReg(..) => {
+                    // this would be an edge from `dummy_source` to
+                    // `dummy_sink`; just ignore it.
+                }
+            }
+        }
+
+        graph
+    }
+
+    fn collect_error_for_expanding_node(
+        &self,
+        graph: &RegionGraph<'tcx>,
+        dup_vec: &mut IndexVec<RegionVid, Option<RegionVid>>,
+        node_idx: RegionVid,
+        errors: &mut Vec<RegionResolutionError<'tcx>>,
+    ) {
+        // Errors in expanding nodes result from a lower-bound that is
+        // not contained by an upper-bound.
+        let (mut lower_bounds, lower_vid_bounds, lower_dup) =
+            self.collect_bounding_regions(graph, node_idx, INCOMING, Some(dup_vec));
+        let (mut upper_bounds, _, upper_dup) =
+            self.collect_bounding_regions(graph, node_idx, OUTGOING, Some(dup_vec));
+
+        if lower_dup || upper_dup {
+            return;
+        }
+
+        // We place free regions first because we are special casing
+        // SubSupConflict(ReFree, ReFree) when reporting error, and so
+        // the user will more likely get a specific suggestion.
+        fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 {
+            match *x.region {
+                ReEarlyBound(_) => 0,
+                ReFree(_) => 1,
+                _ => 2,
+            }
+        }
+        lower_bounds.sort_by_key(region_order_key);
+        upper_bounds.sort_by_key(region_order_key);
+
+        let node_universe = self.var_infos[node_idx].universe;
+
+        for lower_bound in &lower_bounds {
+            let effective_lower_bound = if let ty::RePlaceholder(p) = lower_bound.region {
+                if node_universe.cannot_name(p.universe) {
+                    self.tcx().lifetimes.re_static
+                } else {
+                    lower_bound.region
+                }
+            } else {
+                lower_bound.region
+            };
+
+            for upper_bound in &upper_bounds {
+                if !self.sub_concrete_regions(effective_lower_bound, upper_bound.region) {
+                    let origin = self.var_infos[node_idx].origin;
+                    debug!(
+                        "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
+                         sup: {:?}",
+                        origin, node_idx, lower_bound.region, upper_bound.region
+                    );
+                    errors.push(RegionResolutionError::SubSupConflict(
+                        node_idx,
+                        origin,
+                        lower_bound.origin.clone(),
+                        lower_bound.region,
+                        upper_bound.origin.clone(),
+                        upper_bound.region,
+                    ));
+                    return;
+                }
+            }
+        }
+
+        // If we have a scenario like `exists<'a> { forall<'b> { 'b:
+        // 'a } }`, we wind up without any lower-bound -- all we have
+        // are placeholders as upper bounds, but the universe of the
+        // variable `'a`, or some variable that `'a` has to outlive, doesn't
+        // permit those placeholders.
+        let min_universe = lower_vid_bounds
+            .into_iter()
+            .map(|vid| self.var_infos[vid].universe)
+            .min()
+            .expect("lower_vid_bounds should at least include `node_idx`");
+
+        for upper_bound in &upper_bounds {
+            if let ty::RePlaceholder(p) = upper_bound.region {
+                if min_universe.cannot_name(p.universe) {
+                    let origin = self.var_infos[node_idx].origin;
+                    errors.push(RegionResolutionError::UpperBoundUniverseConflict(
+                        node_idx,
+                        origin,
+                        min_universe,
+                        upper_bound.origin.clone(),
+                        upper_bound.region,
+                    ));
+                    return;
+                }
+            }
+        }
+
+        // Errors in earlier passes can yield error variables without
+        // resolution errors here; delay ICE in favor of those errors.
+        self.tcx().sess.delay_span_bug(
+            self.var_infos[node_idx].origin.span(),
+            &format!(
+                "collect_error_for_expanding_node() could not find \
+                 error for var {:?} in universe {:?}, lower_bounds={:#?}, \
+                 upper_bounds={:#?}",
+                node_idx, node_universe, lower_bounds, upper_bounds
+            ),
+        );
+    }
+
+    /// Collects all regions that "bound" the variable `orig_node_idx` in the
+    /// given direction.
+    ///
+    /// If `dup_vec` is `Some` it's used to track duplicates between successive
+    /// calls of this function.
+    ///
+    /// The return tuple fields are:
+    /// - a list of all concrete regions bounding the given region.
+    /// - the set of all region variables bounding the given region.
+    /// - a `bool` that's true if the returned region variables overlap with
+    ///   those returned by a previous call for another region.
+    fn collect_bounding_regions(
+        &self,
+        graph: &RegionGraph<'tcx>,
+        orig_node_idx: RegionVid,
+        dir: Direction,
+        mut dup_vec: Option<&mut IndexVec<RegionVid, Option<RegionVid>>>,
+    ) -> (Vec<RegionAndOrigin<'tcx>>, FxHashSet<RegionVid>, bool) {
+        struct WalkState<'tcx> {
+            set: FxHashSet<RegionVid>,
+            stack: Vec<RegionVid>,
+            result: Vec<RegionAndOrigin<'tcx>>,
+            dup_found: bool,
+        }
+        let mut state = WalkState {
+            set: Default::default(),
+            stack: vec![orig_node_idx],
+            result: Vec::new(),
+            dup_found: false,
+        };
+        state.set.insert(orig_node_idx);
+
+        // to start off the process, walk the source node in the
+        // direction specified
+        process_edges(&self.data, &mut state, graph, orig_node_idx, dir);
+
+        while let Some(node_idx) = state.stack.pop() {
+            // check whether we've visited this node on some previous walk
+            if let Some(dup_vec) = &mut dup_vec {
+                if dup_vec[node_idx].is_none() {
+                    dup_vec[node_idx] = Some(orig_node_idx);
+                } else if dup_vec[node_idx] != Some(orig_node_idx) {
+                    state.dup_found = true;
+                }
+
+                debug!(
+                    "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})",
+                    orig_node_idx, node_idx
+                );
+            }
+
+            process_edges(&self.data, &mut state, graph, node_idx, dir);
+        }
+
+        let WalkState { result, dup_found, set, .. } = state;
+        return (result, set, dup_found);
+
+        fn process_edges<'tcx>(
+            this: &RegionConstraintData<'tcx>,
+            state: &mut WalkState<'tcx>,
+            graph: &RegionGraph<'tcx>,
+            source_vid: RegionVid,
+            dir: Direction,
+        ) {
+            debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir);
+
+            let source_node_index = NodeIndex(source_vid.index() as usize);
+            for (_, edge) in graph.adjacent_edges(source_node_index, dir) {
+                match edge.data {
+                    Constraint::VarSubVar(from_vid, to_vid) => {
+                        let opp_vid = if from_vid == source_vid { to_vid } else { from_vid };
+                        if state.set.insert(opp_vid) {
+                            state.stack.push(opp_vid);
+                        }
+                    }
+
+                    Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => {
+                        state.result.push(RegionAndOrigin {
+                            region,
+                            origin: this.constraints.get(&edge.data).unwrap().clone(),
+                        });
+                    }
+
+                    Constraint::RegSubReg(..) => panic!(
+                        "cannot reach reg-sub-reg edge in region inference \
+                         post-processing"
+                    ),
+                }
+            }
+        }
+    }
+
+    fn bound_is_met(
+        &self,
+        bound: &VerifyBound<'tcx>,
+        var_values: &LexicalRegionResolutions<'tcx>,
+        generic_ty: Ty<'tcx>,
+        min: ty::Region<'tcx>,
+    ) -> bool {
+        match bound {
+            VerifyBound::IfEq(k, b) => {
+                (var_values.normalize(self.region_rels.tcx, *k) == generic_ty)
+                    && self.bound_is_met(b, var_values, generic_ty, min)
+            }
+
+            VerifyBound::OutlivedBy(r) => {
+                self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r))
+            }
+
+            VerifyBound::IsEmpty => {
+                matches!(min, ty::ReEmpty(_))
+            }
+
+            VerifyBound::AnyBound(bs) => {
+                bs.iter().any(|b| self.bound_is_met(b, var_values, generic_ty, min))
+            }
+
+            VerifyBound::AllBounds(bs) => {
+                bs.iter().all(|b| self.bound_is_met(b, var_values, generic_ty, min))
+            }
+        }
+    }
+}
+
+impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin)
+    }
+}
+
+impl<'tcx> LexicalRegionResolutions<'tcx> {
+    fn normalize<T>(&self, tcx: TyCtxt<'tcx>, value: T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        tcx.fold_regions(value, &mut false, |r, _db| match r {
+            ty::ReVar(rid) => self.resolve_var(*rid),
+            _ => r,
+        })
+    }
+
+    fn value(&self, rid: RegionVid) -> &VarValue<'tcx> {
+        &self.values[rid]
+    }
+
+    fn value_mut(&mut self, rid: RegionVid) -> &mut VarValue<'tcx> {
+        &mut self.values[rid]
+    }
+
+    pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> {
+        let result = match self.values[rid] {
+            VarValue::Value(r) => r,
+            VarValue::ErrorValue => self.error_region,
+        };
+        debug!("resolve_var({:?}) = {:?}", rid, result);
+        result
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs
new file mode 100644
index 00000000000..a08323535c5
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/lub.rs
@@ -0,0 +1,126 @@
+use super::combine::CombineFields;
+use super::lattice::{self, LatticeDir};
+use super::InferCtxt;
+use super::Subtype;
+
+use crate::infer::combine::ConstEquateRelation;
+use crate::traits::ObligationCause;
+use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+/// "Least upper bound" (common supertype)
+pub struct Lub<'combine, 'infcx, 'tcx> {
+    fields: &'combine mut CombineFields<'infcx, 'tcx>,
+    a_is_expected: bool,
+}
+
+impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> {
+    pub fn new(
+        fields: &'combine mut CombineFields<'infcx, 'tcx>,
+        a_is_expected: bool,
+    ) -> Lub<'combine, 'infcx, 'tcx> {
+        Lub { fields, a_is_expected }
+    }
+}
+
+impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
+    fn tag(&self) -> &'static str {
+        "Lub"
+    }
+
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.fields.tcx()
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.fields.param_env
+    }
+
+    fn a_is_expected(&self) -> bool {
+        self.a_is_expected
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        match variance {
+            ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
+            ty::Covariant => self.relate(a, b),
+            // FIXME(#41044) -- not correct, need test
+            ty::Bivariant => Ok(a),
+            ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b),
+        }
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        lattice::super_lattice_tys(self, a, b)
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
+
+        let origin = Subtype(box self.fields.trace.clone());
+        Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions(
+            self.tcx(),
+            origin,
+            a,
+            b,
+        ))
+    }
+
+    fn consts(
+        &mut self,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        self.fields.infcx.super_combine_consts(self, a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        debug!("binders(a={:?}, b={:?})", a, b);
+
+        // When higher-ranked types are involved, computing the LUB is
+        // very challenging, switch to invariance. This is obviously
+        // overly conservative but works ok in practice.
+        self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
+        Ok(a)
+    }
+}
+
+impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> {
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
+    }
+}
+
+impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> {
+    fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> {
+        self.fields.infcx
+    }
+
+    fn cause(&self) -> &ObligationCause<'tcx> {
+        &self.fields.trace.cause
+    }
+
+    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);
+        sub.relate(a, v)?;
+        sub.relate(b, v)?;
+        Ok(())
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
new file mode 100644
index 00000000000..f0d63f512fc
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -0,0 +1,1768 @@
+pub use self::freshen::TypeFreshener;
+pub use self::LateBoundRegionConversionTime::*;
+pub use self::RegionVariableOrigin::*;
+pub use self::SubregionOrigin::*;
+pub use self::ValuePairs::*;
+
+pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};
+
+use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine};
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::undo_log::Rollback;
+use rustc_data_structures::unify as ut;
+use rustc_errors::DiagnosticBuilder;
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
+use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
+use rustc_middle::mir::interpret::EvalToConstValueResult;
+use rustc_middle::traits::select;
+use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
+use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
+use rustc_middle::ty::relate::RelateResult;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef};
+pub use rustc_middle::ty::IntVarValue;
+use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt};
+use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid};
+use rustc_session::config::BorrowckMode;
+use rustc_span::symbol::Symbol;
+use rustc_span::Span;
+
+use std::cell::{Cell, Ref, RefCell};
+use std::collections::BTreeMap;
+use std::fmt;
+
+use self::combine::CombineFields;
+use self::free_regions::RegionRelations;
+use self::lexical_region_resolve::LexicalRegionResolutions;
+use self::outlives::env::OutlivesEnvironment;
+use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, VerifyBound};
+use self::region_constraints::{
+    RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot,
+};
+use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+
+pub mod at;
+pub mod canonical;
+mod combine;
+mod equate;
+pub mod error_reporting;
+pub mod free_regions;
+mod freshen;
+mod fudge;
+mod glb;
+mod higher_ranked;
+pub mod lattice;
+mod lexical_region_resolve;
+mod lub;
+pub mod nll_relate;
+pub mod outlives;
+pub mod region_constraints;
+pub mod resolve;
+mod sub;
+pub mod type_variable;
+mod undo_log;
+
+use crate::infer::canonical::OriginalQueryValues;
+pub use rustc_middle::infer::unify_key;
+
+#[must_use]
+#[derive(Debug)]
+pub struct InferOk<'tcx, T> {
+    pub value: T,
+    pub obligations: PredicateObligations<'tcx>,
+}
+pub type InferResult<'tcx, T> = Result<InferOk<'tcx, T>, TypeError<'tcx>>;
+
+pub type Bound<T> = Option<T>;
+pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result"
+pub type FixupResult<'tcx, T> = Result<T, FixupError<'tcx>>; // "fixup result"
+
+pub(crate) type UnificationTable<'a, 'tcx, T> = ut::UnificationTable<
+    ut::InPlace<T, &'a mut ut::UnificationStorage<T>, &'a mut InferCtxtUndoLogs<'tcx>>,
+>;
+
+/// How we should handle region solving.
+///
+/// This is used so that the region values inferred by HIR region solving are
+/// not exposed, and so that we can avoid doing work in HIR typeck that MIR
+/// typeck will also do.
+#[derive(Copy, Clone, Debug)]
+pub enum RegionckMode {
+    /// The default mode: report region errors, don't erase regions.
+    Solve,
+    /// Erase the results of region after solving.
+    Erase {
+        /// A flag that is used to suppress region errors, when we are doing
+        /// region checks that the NLL borrow checker will also do -- it might
+        /// be set to true.
+        suppress_errors: bool,
+    },
+}
+
+impl Default for RegionckMode {
+    fn default() -> Self {
+        RegionckMode::Solve
+    }
+}
+
+impl RegionckMode {
+    /// Indicates that the MIR borrowck will repeat these region
+    /// checks, so we should ignore errors if NLL is (unconditionally)
+    /// enabled.
+    pub fn for_item_body(tcx: TyCtxt<'_>) -> Self {
+        // FIXME(Centril): Once we actually remove `::Migrate` also make
+        // this always `true` and then proceed to eliminate the dead code.
+        match tcx.borrowck_mode() {
+            // If we're on Migrate mode, report AST region errors
+            BorrowckMode::Migrate => RegionckMode::Erase { suppress_errors: false },
+
+            // If we're on MIR, don't report AST region errors as they should be reported by NLL
+            BorrowckMode::Mir => RegionckMode::Erase { suppress_errors: true },
+        }
+    }
+}
+
+/// This type contains all the things within `InferCtxt` that sit within a
+/// `RefCell` and are involved with taking/rolling back snapshots. Snapshot
+/// operations are hot enough that we want only one call to `borrow_mut` per
+/// call to `start_snapshot` and `rollback_to`.
+pub struct InferCtxtInner<'tcx> {
+    /// Cache for projections. This cache is snapshotted along with the infcx.
+    ///
+    /// Public so that `traits::project` can use it.
+    pub projection_cache: traits::ProjectionCacheStorage<'tcx>,
+
+    /// We instantiate `UnificationTable` with `bounds<Ty>` because the types
+    /// that might instantiate a general type variable have an order,
+    /// represented by its upper and lower bounds.
+    type_variable_storage: type_variable::TypeVariableStorage<'tcx>,
+
+    /// Map from const parameter variable to the kind of const it represents.
+    const_unification_storage: ut::UnificationTableStorage<ty::ConstVid<'tcx>>,
+
+    /// Map from integral variable to the kind of integer it represents.
+    int_unification_storage: ut::UnificationTableStorage<ty::IntVid>,
+
+    /// Map from floating variable to the kind of float it represents.
+    float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>,
+
+    /// Tracks the set of region variables and the constraints between them.
+    /// This is initially `Some(_)` but when
+    /// `resolve_regions_and_report_errors` is invoked, this gets set to `None`
+    /// -- further attempts to perform unification, etc., may fail if new
+    /// region constraints would've been added.
+    region_constraint_storage: Option<RegionConstraintStorage<'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
+    /// instantiated type parameters. So if you had a struct defined
+    /// like
+    ///
+    ///     struct Foo<T:'static> { ... }
+    ///
+    /// then in some expression `let x = Foo { ... }` it will
+    /// instantiate the type parameter `T` with a fresh type `$0`. At
+    /// the same time, it will record a region obligation of
+    /// `$0:'static`. This will get checked later by regionck. (We
+    /// can't generally check these things right away because we have
+    /// to wait until types are resolved.)
+    ///
+    /// These are stored in a map keyed to the id of the innermost
+    /// enclosing fn body / static initializer expression. This is
+    /// because the location where the obligation was incurred can be
+    /// relevant with respect to which sublifetime assumptions are in
+    /// place. The reason that we store under the fn-id, and not
+    /// something more fine-grained, is so that it is easier for
+    /// regionck to be sure that it has found *all* the region
+    /// obligations (otherwise, it's easy to fail to walk to a
+    /// particular node-id).
+    ///
+    /// Before running `resolve_regions_and_report_errors`, the creator
+    /// of the inference context is expected to invoke
+    /// `process_region_obligations` (defined in `self::region_obligations`)
+    /// for each body-id in this map, which will process the
+    /// obligations within. This is expected to be done 'late enough'
+    /// that all type inference variables have been bound and so forth.
+    region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>,
+
+    undo_log: InferCtxtUndoLogs<'tcx>,
+}
+
+impl<'tcx> InferCtxtInner<'tcx> {
+    fn new() -> InferCtxtInner<'tcx> {
+        InferCtxtInner {
+            projection_cache: Default::default(),
+            type_variable_storage: type_variable::TypeVariableStorage::new(),
+            undo_log: InferCtxtUndoLogs::default(),
+            const_unification_storage: ut::UnificationTableStorage::new(),
+            int_unification_storage: ut::UnificationTableStorage::new(),
+            float_unification_storage: ut::UnificationTableStorage::new(),
+            region_constraint_storage: Some(RegionConstraintStorage::new()),
+            region_obligations: vec![],
+        }
+    }
+
+    #[inline]
+    pub fn region_obligations(&self) -> &[(hir::HirId, RegionObligation<'tcx>)] {
+        &self.region_obligations
+    }
+
+    #[inline]
+    pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> {
+        self.projection_cache.with_log(&mut self.undo_log)
+    }
+
+    #[inline]
+    fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> {
+        self.type_variable_storage.with_log(&mut self.undo_log)
+    }
+
+    #[inline]
+    fn int_unification_table(
+        &mut self,
+    ) -> ut::UnificationTable<
+        ut::InPlace<
+            ty::IntVid,
+            &mut ut::UnificationStorage<ty::IntVid>,
+            &mut InferCtxtUndoLogs<'tcx>,
+        >,
+    > {
+        self.int_unification_storage.with_log(&mut self.undo_log)
+    }
+
+    #[inline]
+    fn float_unification_table(
+        &mut self,
+    ) -> ut::UnificationTable<
+        ut::InPlace<
+            ty::FloatVid,
+            &mut ut::UnificationStorage<ty::FloatVid>,
+            &mut InferCtxtUndoLogs<'tcx>,
+        >,
+    > {
+        self.float_unification_storage.with_log(&mut self.undo_log)
+    }
+
+    #[inline]
+    fn const_unification_table(
+        &mut self,
+    ) -> ut::UnificationTable<
+        ut::InPlace<
+            ty::ConstVid<'tcx>,
+            &mut ut::UnificationStorage<ty::ConstVid<'tcx>>,
+            &mut InferCtxtUndoLogs<'tcx>,
+        >,
+    > {
+        self.const_unification_storage.with_log(&mut self.undo_log)
+    }
+
+    #[inline]
+    pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> {
+        self.region_constraint_storage
+            .as_mut()
+            .expect("region constraints already solved")
+            .with_log(&mut self.undo_log)
+    }
+}
+
+pub struct InferCtxt<'a, 'tcx> {
+    pub tcx: TyCtxt<'tcx>,
+
+    /// During type-checking/inference of a body, `in_progress_typeck_results`
+    /// contains a reference to the typeck results being built up, which are
+    /// used for reading closure kinds/signatures as they are inferred,
+    /// and for error reporting logic to read arbitrary node types.
+    pub in_progress_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>,
+
+    pub inner: RefCell<InferCtxtInner<'tcx>>,
+
+    /// If set, this flag causes us to skip the 'leak check' during
+    /// higher-ranked subtyping operations. This flag is a temporary one used
+    /// to manage the removal of the leak-check: for the time being, we still run the
+    /// leak-check, but we issue warnings. This flag can only be set to true
+    /// when entering a snapshot.
+    skip_leak_check: Cell<bool>,
+
+    /// Once region inference is done, the values for each variable.
+    lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>,
+
+    /// Caches the results of trait selection. This cache is used
+    /// for things that have to do with the parameters in scope.
+    pub selection_cache: select::SelectionCache<'tcx>,
+
+    /// Caches the results of trait evaluation.
+    pub evaluation_cache: select::EvaluationCache<'tcx>,
+
+    /// 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_closure_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>,
+
+    /// When an error occurs, we want to avoid reporting "derived"
+    /// errors that are due to this original failure. Normally, we
+    /// handle this with the `err_count_on_creation` count, which
+    /// basically just tracks how many errors were reported when we
+    /// started type-checking a fn and checks to see if any new errors
+    /// have been reported since then. Not great, but it works.
+    ///
+    /// However, when errors originated in other passes -- notably
+    /// resolve -- this heuristic breaks down. Therefore, we have this
+    /// auxiliary flag that one can set whenever one creates a
+    /// type-error that is due to an error in a prior pass.
+    ///
+    /// Don't read this flag directly, call `is_tainted_by_errors()`
+    /// and `set_tainted_by_errors()`.
+    tainted_by_errors_flag: Cell<bool>,
+
+    /// Track how many errors were reported when this infcx is created.
+    /// If the number of errors increases, that's also a sign (line
+    /// `tained_by_errors`) to avoid reporting certain kinds of errors.
+    // FIXME(matthewjasper) Merge into `tainted_by_errors_flag`
+    err_count_on_creation: usize,
+
+    /// This flag is true while there is an active snapshot.
+    in_snapshot: Cell<bool>,
+
+    /// What is the innermost universe we have created? Starts out as
+    /// `UniverseIndex::root()` but grows from there as we enter
+    /// universal quantifiers.
+    ///
+    /// N.B., 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.
+    universe: Cell<ty::UniverseIndex>,
+}
+
+/// See the `error_reporting` module for more details.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)]
+pub enum ValuePairs<'tcx> {
+    Types(ExpectedFound<Ty<'tcx>>),
+    Regions(ExpectedFound<ty::Region<'tcx>>),
+    Consts(ExpectedFound<&'tcx ty::Const<'tcx>>),
+    TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>),
+    PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>),
+}
+
+/// The trace designates the path through inference that we took to
+/// encounter an error or subtyping constraint.
+///
+/// See the `error_reporting` module for more details.
+#[derive(Clone, Debug)]
+pub struct TypeTrace<'tcx> {
+    cause: ObligationCause<'tcx>,
+    values: ValuePairs<'tcx>,
+}
+
+/// The origin of a `r1 <= r2` constraint.
+///
+/// See `error_reporting` module for more details
+#[derive(Clone, Debug)]
+pub enum SubregionOrigin<'tcx> {
+    /// Arose from a subtyping relation
+    Subtype(Box<TypeTrace<'tcx>>),
+
+    /// When casting `&'a T` to an `&'b Trait` object,
+    /// relating `'a` to `'b`
+    RelateObjectBound(Span),
+
+    /// Some type parameter was instantiated with the given type,
+    /// and that type must outlive some region.
+    RelateParamBound(Span, Ty<'tcx>, Option<Span>),
+
+    /// The given region parameter was instantiated with a region
+    /// that must outlive some other region.
+    RelateRegionParamBound(Span),
+
+    /// Creating a pointer `b` to contents of another reference
+    Reborrow(Span),
+
+    /// Creating a pointer `b` to contents of an upvar
+    ReborrowUpvar(Span, ty::UpvarId),
+
+    /// Data with type `Ty<'tcx>` was borrowed
+    DataBorrowed(Ty<'tcx>, Span),
+
+    /// (&'a &'b T) where a >= b
+    ReferenceOutlivesReferent(Ty<'tcx>, Span),
+
+    /// Region in return type of invoked fn must enclose call
+    CallReturn(Span),
+
+    /// Comparing the signature and requirements of an impl method against
+    /// the containing trait.
+    CompareImplMethodObligation {
+        span: Span,
+        item_name: Symbol,
+        impl_item_def_id: DefId,
+        trait_item_def_id: DefId,
+    },
+}
+
+// `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+static_assert_size!(SubregionOrigin<'_>, 32);
+
+/// Times when we replace late-bound regions with variables:
+#[derive(Clone, Copy, Debug)]
+pub enum LateBoundRegionConversionTime {
+    /// when a fn is called
+    FnCall,
+
+    /// when two higher-ranked types are compared
+    HigherRankedType,
+
+    /// when projecting an associated type
+    AssocTypeProjection(DefId),
+}
+
+/// Reasons to create a region inference variable
+///
+/// See `error_reporting` module for more details
+#[derive(Copy, Clone, Debug)]
+pub enum RegionVariableOrigin {
+    /// Region variables created for ill-categorized reasons,
+    /// mostly indicates places in need of refactoring
+    MiscVariable(Span),
+
+    /// Regions created by a `&P` or `[...]` pattern
+    PatternRegion(Span),
+
+    /// Regions created by `&` operator
+    AddrOfRegion(Span),
+
+    /// Regions created as part of an autoref of a method receiver
+    Autoref(Span, ty::AssocItem),
+
+    /// Regions created as part of an automatic coercion
+    Coercion(Span),
+
+    /// Region variables created as the values for early-bound regions
+    EarlyBoundRegion(Span, Symbol),
+
+    /// Region variables created for bound regions
+    /// in a function or method that is called
+    LateBoundRegion(Span, ty::BoundRegionKind, LateBoundRegionConversionTime),
+
+    UpvarRegion(ty::UpvarId, Span),
+
+    /// This origin is used for the inference variables that we create
+    /// during NLL region processing.
+    Nll(NllRegionVariableOrigin),
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum NllRegionVariableOrigin {
+    /// During NLL region processing, we create variables for free
+    /// regions that we encounter in the function signature and
+    /// elsewhere. This origin indices we've got one of those.
+    FreeRegion,
+
+    /// "Universal" instantiation of a higher-ranked region (e.g.,
+    /// from a `for<'a> T` binder). Meant to represent "any region".
+    Placeholder(ty::PlaceholderRegion),
+
+    /// The variable we create to represent `'empty(U0)`.
+    RootEmptyRegion,
+
+    Existential {
+        /// If this is true, then this variable was created to represent a lifetime
+        /// bound in a `for` binder. For example, it might have been created to
+        /// represent the lifetime `'a` in a type like `for<'a> fn(&'a u32)`.
+        /// Such variables are created when we are trying to figure out if there
+        /// is any valid instantiation of `'a` that could fit into some scenario.
+        ///
+        /// This is used to inform error reporting: in the case that we are trying to
+        /// determine whether there is any valid instantiation of a `'a` variable that meets
+        /// some constraint C, we want to blame the "source" of that `for` type,
+        /// rather than blaming the source of the constraint C.
+        from_forall: bool,
+    },
+}
+
+// FIXME(eddyb) investigate overlap between this and `TyOrConstInferVar`.
+#[derive(Copy, Clone, Debug)]
+pub enum FixupError<'tcx> {
+    UnresolvedIntTy(IntVid),
+    UnresolvedFloatTy(FloatVid),
+    UnresolvedTy(TyVid),
+    UnresolvedConst(ConstVid<'tcx>),
+}
+
+/// See the `region_obligations` field for more information.
+#[derive(Clone)]
+pub struct RegionObligation<'tcx> {
+    pub sub_region: ty::Region<'tcx>,
+    pub sup_type: Ty<'tcx>,
+    pub origin: SubregionOrigin<'tcx>,
+}
+
+impl<'tcx> fmt::Display for FixupError<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        use self::FixupError::*;
+
+        match *self {
+            UnresolvedIntTy(_) => write!(
+                f,
+                "cannot determine the type of this integer; \
+                 add a suffix to specify the type explicitly"
+            ),
+            UnresolvedFloatTy(_) => write!(
+                f,
+                "cannot determine the type of this number; \
+                 add a suffix to specify the type explicitly"
+            ),
+            UnresolvedTy(_) => write!(f, "unconstrained type"),
+            UnresolvedConst(_) => write!(f, "unconstrained const value"),
+        }
+    }
+}
+
+/// Helper type of a temporary returned by `tcx.infer_ctxt()`.
+/// Necessary because we can't write the following bound:
+/// `F: for<'b, 'tcx> where 'tcx FnOnce(InferCtxt<'b, 'tcx>)`.
+pub struct InferCtxtBuilder<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>,
+}
+
+pub trait TyCtxtInferExt<'tcx> {
+    fn infer_ctxt(self) -> InferCtxtBuilder<'tcx>;
+}
+
+impl TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
+    fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> {
+        InferCtxtBuilder { tcx: self, fresh_typeck_results: None }
+    }
+}
+
+impl<'tcx> InferCtxtBuilder<'tcx> {
+    /// Used only by `rustc_typeck` during body type-checking/inference,
+    /// will initialize `in_progress_typeck_results` with fresh `TypeckResults`.
+    pub fn with_fresh_in_progress_typeck_results(mut self, table_owner: LocalDefId) -> Self {
+        self.fresh_typeck_results = Some(RefCell::new(ty::TypeckResults::new(table_owner)));
+        self
+    }
+
+    /// Given a canonical value `C` as a starting point, create an
+    /// inference context that contains each of the bound values
+    /// within instantiated as a fresh variable. The `f` closure is
+    /// invoked with the new infcx, along with the instantiated value
+    /// `V` and a substitution `S`. This substitution `S` maps from
+    /// the bound values in `C` to their instantiated values in `V`
+    /// (in other words, `S(C) = V`).
+    pub fn enter_with_canonical<T, R>(
+        &mut self,
+        span: Span,
+        canonical: &Canonical<'tcx, T>,
+        f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>, T, CanonicalVarValues<'tcx>) -> R,
+    ) -> R
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.enter(|infcx| {
+            let (value, subst) =
+                infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical);
+            f(infcx, value, subst)
+        })
+    }
+
+    pub fn enter<R>(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R {
+        let InferCtxtBuilder { tcx, ref fresh_typeck_results } = *self;
+        let in_progress_typeck_results = fresh_typeck_results.as_ref();
+        f(InferCtxt {
+            tcx,
+            in_progress_typeck_results,
+            inner: RefCell::new(InferCtxtInner::new()),
+            lexical_region_resolutions: RefCell::new(None),
+            selection_cache: Default::default(),
+            evaluation_cache: Default::default(),
+            reported_trait_errors: Default::default(),
+            reported_closure_mismatch: Default::default(),
+            tainted_by_errors_flag: Cell::new(false),
+            err_count_on_creation: tcx.sess.err_count(),
+            in_snapshot: Cell::new(false),
+            skip_leak_check: Cell::new(false),
+            universe: Cell::new(ty::UniverseIndex::ROOT),
+        })
+    }
+}
+
+impl<'tcx, T> InferOk<'tcx, T> {
+    pub fn unit(self) -> InferOk<'tcx, ()> {
+        InferOk { value: (), obligations: self.obligations }
+    }
+
+    /// Extracts `value`, registering any obligations into `fulfill_cx`.
+    pub fn into_value_registering_obligations(
+        self,
+        infcx: &InferCtxt<'_, 'tcx>,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> T {
+        let InferOk { value, obligations } = self;
+        for obligation in obligations {
+            fulfill_cx.register_predicate_obligation(infcx, obligation);
+        }
+        value
+    }
+}
+
+impl<'tcx> InferOk<'tcx, ()> {
+    pub fn into_obligations(self) -> PredicateObligations<'tcx> {
+        self.obligations
+    }
+}
+
+#[must_use = "once you start a snapshot, you should always consume it"]
+pub struct CombinedSnapshot<'a, 'tcx> {
+    undo_snapshot: Snapshot<'tcx>,
+    region_constraints_snapshot: RegionSnapshot,
+    universe: ty::UniverseIndex,
+    was_in_snapshot: bool,
+    _in_progress_typeck_results: Option<Ref<'a, ty::TypeckResults<'tcx>>>,
+}
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    pub fn is_in_snapshot(&self) -> bool {
+        self.in_snapshot.get()
+    }
+
+    pub fn freshen<T: TypeFoldable<'tcx>>(&self, t: T) -> T {
+        t.fold_with(&mut self.freshener())
+    }
+
+    pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool {
+        match *ty.kind() {
+            ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid),
+            _ => false,
+        }
+    }
+
+    pub fn freshener<'b>(&'b self) -> TypeFreshener<'b, 'tcx> {
+        freshen::TypeFreshener::new(self, false)
+    }
+
+    /// Like `freshener`, but does not replace `'static` regions.
+    pub fn freshener_keep_static<'b>(&'b self) -> TypeFreshener<'b, 'tcx> {
+        freshen::TypeFreshener::new(self, true)
+    }
+
+    pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric {
+        use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
+        use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
+        match *ty.kind() {
+            ty::Infer(ty::IntVar(vid)) => {
+                if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() {
+                    Neither
+                } else {
+                    UnconstrainedInt
+                }
+            }
+            ty::Infer(ty::FloatVar(vid)) => {
+                if self.inner.borrow_mut().float_unification_table().probe_value(vid).is_some() {
+                    Neither
+                } else {
+                    UnconstrainedFloat
+                }
+            }
+            _ => Neither,
+        }
+    }
+
+    pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> {
+        let mut inner = self.inner.borrow_mut();
+        let mut vars: Vec<Ty<'_>> = inner
+            .type_variables()
+            .unsolved_variables()
+            .into_iter()
+            .map(|t| self.tcx.mk_ty_var(t))
+            .collect();
+        vars.extend(
+            (0..inner.int_unification_table().len())
+                .map(|i| ty::IntVid { index: i as u32 })
+                .filter(|&vid| inner.int_unification_table().probe_value(vid).is_none())
+                .map(|v| self.tcx.mk_int_var(v)),
+        );
+        vars.extend(
+            (0..inner.float_unification_table().len())
+                .map(|i| ty::FloatVid { index: i as u32 })
+                .filter(|&vid| inner.float_unification_table().probe_value(vid).is_none())
+                .map(|v| self.tcx.mk_float_var(v)),
+        );
+        vars
+    }
+
+    fn combine_fields(
+        &'a self,
+        trace: TypeTrace<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> CombineFields<'a, 'tcx> {
+        CombineFields {
+            infcx: self,
+            trace,
+            cause: None,
+            param_env,
+            obligations: PredicateObligations::new(),
+        }
+    }
+
+    /// Clear the "currently in a snapshot" flag, invoke the closure,
+    /// then restore the flag to its original value. This flag is a
+    /// debugging measure designed to detect cases where we start a
+    /// snapshot, create type variables, and register obligations
+    /// which may involve those type variables in the fulfillment cx,
+    /// potentially leaving "dangling type variables" behind.
+    /// In such cases, an assertion will fail when attempting to
+    /// register obligations, within a snapshot. Very useful, much
+    /// better than grovelling through megabytes of `RUSTC_LOG` output.
+    ///
+    /// HOWEVER, in some cases the flag is unhelpful. In particular, we
+    /// sometimes create a "mini-fulfilment-cx" in which we enroll
+    /// obligations. As long as this fulfillment cx is fully drained
+    /// before we return, this is not a problem, as there won't be any
+    /// escaping obligations in the main cx. In those cases, you can
+    /// use this function.
+    pub fn save_and_restore_in_snapshot_flag<F, R>(&self, func: F) -> R
+    where
+        F: FnOnce(&Self) -> R,
+    {
+        let flag = self.in_snapshot.replace(false);
+        let result = func(self);
+        self.in_snapshot.set(flag);
+        result
+    }
+
+    fn start_snapshot(&self) -> CombinedSnapshot<'a, 'tcx> {
+        debug!("start_snapshot()");
+
+        let in_snapshot = self.in_snapshot.replace(true);
+
+        let mut inner = self.inner.borrow_mut();
+
+        CombinedSnapshot {
+            undo_snapshot: inner.undo_log.start_snapshot(),
+            region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(),
+            universe: self.universe(),
+            was_in_snapshot: in_snapshot,
+            // Borrow typeck results "in progress" (i.e., during typeck)
+            // to ban writes from within a snapshot to them.
+            _in_progress_typeck_results: self
+                .in_progress_typeck_results
+                .map(|typeck_results| typeck_results.borrow()),
+        }
+    }
+
+    fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) {
+        debug!("rollback_to(cause={})", cause);
+        let CombinedSnapshot {
+            undo_snapshot,
+            region_constraints_snapshot,
+            universe,
+            was_in_snapshot,
+            _in_progress_typeck_results,
+        } = snapshot;
+
+        self.in_snapshot.set(was_in_snapshot);
+        self.universe.set(universe);
+
+        let mut inner = self.inner.borrow_mut();
+        inner.rollback_to(undo_snapshot);
+        inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot);
+    }
+
+    fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) {
+        debug!("commit_from()");
+        let CombinedSnapshot {
+            undo_snapshot,
+            region_constraints_snapshot: _,
+            universe: _,
+            was_in_snapshot,
+            _in_progress_typeck_results,
+        } = snapshot;
+
+        self.in_snapshot.set(was_in_snapshot);
+
+        self.inner.borrow_mut().commit(undo_snapshot);
+    }
+
+    /// Executes `f` and commit the bindings.
+    pub fn commit_unconditionally<R, F>(&self, f: F) -> R
+    where
+        F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
+    {
+        debug!("commit_unconditionally()");
+        let snapshot = self.start_snapshot();
+        let r = f(&snapshot);
+        self.commit_from(snapshot);
+        r
+    }
+
+    /// 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<'a, 'tcx>) -> Result<T, E>,
+    {
+        debug!("commit_if_ok()");
+        let snapshot = self.start_snapshot();
+        let r = f(&snapshot);
+        debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok());
+        match r {
+            Ok(_) => {
+                self.commit_from(snapshot);
+            }
+            Err(_) => {
+                self.rollback_to("commit_if_ok -- error", snapshot);
+            }
+        }
+        r
+    }
+
+    /// Execute `f` then unroll any bindings it creates.
+    pub fn probe<R, F>(&self, f: F) -> R
+    where
+        F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
+    {
+        debug!("probe()");
+        let snapshot = self.start_snapshot();
+        let r = f(&snapshot);
+        self.rollback_to("probe", snapshot);
+        r
+    }
+
+    /// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
+    pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
+    where
+        F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
+    {
+        debug!("probe()");
+        let snapshot = self.start_snapshot();
+        let was_skip_leak_check = self.skip_leak_check.get();
+        if should_skip {
+            self.skip_leak_check.set(true);
+        }
+        let r = f(&snapshot);
+        self.rollback_to("probe", snapshot);
+        self.skip_leak_check.set(was_skip_leak_check);
+        r
+    }
+
+    /// Scan the constraints produced since `snapshot` began and returns:
+    ///
+    /// - `None` -- if none of them involve "region outlives" constraints
+    /// - `Some(true)` -- if there are `'a: 'b` constraints where `'a` or `'b` is a placeholder
+    /// - `Some(false)` -- if there are `'a: 'b` constraints but none involve placeholders
+    pub fn region_constraints_added_in_snapshot(
+        &self,
+        snapshot: &CombinedSnapshot<'a, 'tcx>,
+    ) -> Option<bool> {
+        self.inner
+            .borrow_mut()
+            .unwrap_region_constraints()
+            .region_constraints_added_in_snapshot(&snapshot.undo_snapshot)
+    }
+
+    pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) {
+        self.inner.borrow_mut().unwrap_region_constraints().add_given(sub, sup);
+    }
+
+    pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx>
+    where
+        T: at::ToTrace<'tcx>,
+    {
+        let origin = &ObligationCause::dummy();
+        self.probe(|_| {
+            self.at(origin, param_env).sub(a, b).map(|InferOk { obligations: _, .. }| {
+                // Ignore obligations, since we are unrolling
+                // everything anyway.
+            })
+        })
+    }
+
+    pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx>
+    where
+        T: at::ToTrace<'tcx>,
+    {
+        let origin = &ObligationCause::dummy();
+        self.probe(|_| {
+            self.at(origin, param_env).eq(a, b).map(|InferOk { obligations: _, .. }| {
+                // Ignore obligations, since we are unrolling
+                // everything anyway.
+            })
+        })
+    }
+
+    pub fn sub_regions(
+        &self,
+        origin: SubregionOrigin<'tcx>,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) {
+        debug!("sub_regions({:?} <: {:?})", a, b);
+        self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b);
+    }
+
+    /// Require that the region `r` be equal to one of the regions in
+    /// the set `regions`.
+    pub fn member_constraint(
+        &self,
+        opaque_type_def_id: DefId,
+        definition_span: Span,
+        hidden_ty: Ty<'tcx>,
+        region: ty::Region<'tcx>,
+        in_regions: &Lrc<Vec<ty::Region<'tcx>>>,
+    ) {
+        debug!("member_constraint({:?} <: {:?})", region, in_regions);
+        self.inner.borrow_mut().unwrap_region_constraints().member_constraint(
+            opaque_type_def_id,
+            definition_span,
+            hidden_ty,
+            region,
+            in_regions,
+        );
+    }
+
+    pub fn subtype_predicate(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        predicate: ty::PolySubtypePredicate<'tcx>,
+    ) -> Option<InferResult<'tcx, ()>> {
+        // Subtle: it's ok to skip the binder here and resolve because
+        // `shallow_resolve` just ignores anything that is not a type
+        // variable, and because type variable's can't (at present, at
+        // least) capture any of the things bound by this binder.
+        //
+        // NOTE(nmatsakis): really, there is no *particular* reason to do this
+        // `shallow_resolve` here except as a micro-optimization.
+        // Naturally I could not resist.
+        let two_unbound_type_vars = {
+            let a = self.shallow_resolve(predicate.skip_binder().a);
+            let b = self.shallow_resolve(predicate.skip_binder().b);
+            a.is_ty_var() && b.is_ty_var()
+        };
+
+        if two_unbound_type_vars {
+            // Two unbound type variables? Can't make progress.
+            return None;
+        }
+
+        Some(self.commit_if_ok(|_snapshot| {
+            let ty::SubtypePredicate { a_is_expected, a, b } =
+                self.replace_bound_vars_with_placeholders(predicate);
+
+            let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?;
+
+            Ok(ok.unit())
+        }))
+    }
+
+    pub fn region_outlives_predicate(
+        &self,
+        cause: &traits::ObligationCause<'tcx>,
+        predicate: ty::PolyRegionOutlivesPredicate<'tcx>,
+    ) -> UnitResult<'tcx> {
+        self.commit_if_ok(|_snapshot| {
+            let ty::OutlivesPredicate(r_a, r_b) =
+                self.replace_bound_vars_with_placeholders(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, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
+        self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin)
+    }
+
+    pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
+        self.tcx.mk_ty_var(self.next_ty_var_id(false, origin))
+    }
+
+    pub fn next_ty_var_in_universe(
+        &self,
+        origin: TypeVariableOrigin,
+        universe: ty::UniverseIndex,
+    ) -> Ty<'tcx> {
+        let vid = self.inner.borrow_mut().type_variables().new_var(universe, false, origin);
+        self.tcx.mk_ty_var(vid)
+    }
+
+    pub fn next_diverging_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
+        self.tcx.mk_ty_var(self.next_ty_var_id(true, origin))
+    }
+
+    pub fn next_const_var(
+        &self,
+        ty: Ty<'tcx>,
+        origin: ConstVariableOrigin,
+    ) -> &'tcx ty::Const<'tcx> {
+        self.tcx.mk_const_var(self.next_const_var_id(origin), ty)
+    }
+
+    pub fn next_const_var_in_universe(
+        &self,
+        ty: Ty<'tcx>,
+        origin: ConstVariableOrigin,
+        universe: ty::UniverseIndex,
+    ) -> &'tcx ty::Const<'tcx> {
+        let vid = self
+            .inner
+            .borrow_mut()
+            .const_unification_table()
+            .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } });
+        self.tcx.mk_const_var(vid, ty)
+    }
+
+    pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> {
+        self.inner.borrow_mut().const_unification_table().new_key(ConstVarValue {
+            origin,
+            val: ConstVariableValue::Unknown { universe: self.universe() },
+        })
+    }
+
+    fn next_int_var_id(&self) -> IntVid {
+        self.inner.borrow_mut().int_unification_table().new_key(None)
+    }
+
+    pub fn next_int_var(&self) -> Ty<'tcx> {
+        self.tcx.mk_int_var(self.next_int_var_id())
+    }
+
+    fn next_float_var_id(&self) -> FloatVid {
+        self.inner.borrow_mut().float_unification_table().new_key(None)
+    }
+
+    pub fn next_float_var(&self) -> Ty<'tcx> {
+        self.tcx.mk_float_var(self.next_float_var_id())
+    }
+
+    /// Creates a fresh region variable with the next available index.
+    /// The variable will be created in the maximum universe created
+    /// thus far, allowing it to name any region created thus far.
+    pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> {
+        self.next_region_var_in_universe(origin, self.universe())
+    }
+
+    /// Creates a fresh region variable with the next available index
+    /// in the given universe; typically, you can use
+    /// `next_region_var` and just use the maximal universe.
+    pub fn next_region_var_in_universe(
+        &self,
+        origin: RegionVariableOrigin,
+        universe: ty::UniverseIndex,
+    ) -> ty::Region<'tcx> {
+        let region_var =
+            self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe, origin);
+        self.tcx.mk_region(ty::ReVar(region_var))
+    }
+
+    /// Return the universe that the region `r` was created in.  For
+    /// most regions (e.g., `'static`, named regions from the user,
+    /// etc) this is the root universe U0. For inference variables or
+    /// placeholders, however, it will return the universe which which
+    /// they are associated.
+    fn universe_of_region(&self, r: ty::Region<'tcx>) -> ty::UniverseIndex {
+        self.inner.borrow_mut().unwrap_region_constraints().universe(r)
+    }
+
+    /// Number of region variables created so far.
+    pub fn num_region_vars(&self) -> usize {
+        self.inner.borrow_mut().unwrap_region_constraints().num_region_vars()
+    }
+
+    /// Just a convenient wrapper of `next_region_var` for using during NLL.
+    pub fn next_nll_region_var(&self, origin: NllRegionVariableOrigin) -> ty::Region<'tcx> {
+        self.next_region_var(RegionVariableOrigin::Nll(origin))
+    }
+
+    /// Just a convenient wrapper of `next_region_var` for using during NLL.
+    pub fn next_nll_region_var_in_universe(
+        &self,
+        origin: NllRegionVariableOrigin,
+        universe: ty::UniverseIndex,
+    ) -> ty::Region<'tcx> {
+        self.next_region_var_in_universe(RegionVariableOrigin::Nll(origin), universe)
+    }
+
+    pub fn var_for_def(&self, span: Span, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
+        match param.kind {
+            GenericParamDefKind::Lifetime => {
+                // Create a region inference variable for the given
+                // region parameter definition.
+                self.next_region_var(EarlyBoundRegion(span, param.name)).into()
+            }
+            GenericParamDefKind::Type { .. } => {
+                // Create a type inference variable for the given
+                // type parameter definition. The substitutions are
+                // for actual parameters that may be referred to by
+                // the default of this type parameter, if it exists.
+                // e.g., `struct Foo<A, B, C = (A, B)>(...);` when
+                // used in a path such as `Foo::<T, U>::new()` will
+                // use an inference variable for `C` with `[T, U]`
+                // as the substitutions for the default, `(T, U)`.
+                let ty_var_id = self.inner.borrow_mut().type_variables().new_var(
+                    self.universe(),
+                    false,
+                    TypeVariableOrigin {
+                        kind: TypeVariableOriginKind::TypeParameterDefinition(
+                            param.name,
+                            Some(param.def_id),
+                        ),
+                        span,
+                    },
+                );
+
+                self.tcx.mk_ty_var(ty_var_id).into()
+            }
+            GenericParamDefKind::Const { .. } => {
+                let origin = ConstVariableOrigin {
+                    kind: ConstVariableOriginKind::ConstParameterDefinition(
+                        param.name,
+                        param.def_id,
+                    ),
+                    span,
+                };
+                let const_var_id =
+                    self.inner.borrow_mut().const_unification_table().new_key(ConstVarValue {
+                        origin,
+                        val: ConstVariableValue::Unknown { universe: self.universe() },
+                    });
+                self.tcx.mk_const_var(const_var_id, self.tcx.type_of(param.def_id)).into()
+            }
+        }
+    }
+
+    /// 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, span: Span, def_id: DefId) -> SubstsRef<'tcx> {
+        InternalSubsts::for_item(self.tcx, def_id, |param, _| self.var_for_def(span, param))
+    }
+
+    /// Returns `true` if errors have been reported since this infcx was
+    /// created. This is sometimes used as a heuristic to skip
+    /// reporting errors that often occur as a result of earlier
+    /// errors, but where it's hard to be 100% sure (e.g., unresolved
+    /// inference variables, regionck errors).
+    pub fn is_tainted_by_errors(&self) -> bool {
+        debug!(
+            "is_tainted_by_errors(err_count={}, err_count_on_creation={}, \
+             tainted_by_errors_flag={})",
+            self.tcx.sess.err_count(),
+            self.err_count_on_creation,
+            self.tainted_by_errors_flag.get()
+        );
+
+        if self.tcx.sess.err_count() > self.err_count_on_creation {
+            return true; // errors reported since this infcx was made
+        }
+        self.tainted_by_errors_flag.get()
+    }
+
+    /// Set the "tainted by errors" flag to true. We call this when we
+    /// observe an error from a prior pass.
+    pub fn set_tainted_by_errors(&self) {
+        debug!("set_tainted_by_errors()");
+        self.tainted_by_errors_flag.set(true)
+    }
+
+    /// Process the region constraints and report any errors that
+    /// result. After this, no more unification operations should be
+    /// done -- or the compiler will panic -- but it is legal to use
+    /// `resolve_vars_if_possible` as well as `fully_resolve`.
+    pub fn resolve_regions_and_report_errors(
+        &self,
+        region_context: DefId,
+        outlives_env: &OutlivesEnvironment<'tcx>,
+        mode: RegionckMode,
+    ) {
+        let (var_infos, data) = {
+            let mut inner = self.inner.borrow_mut();
+            let inner = &mut *inner;
+            assert!(
+                self.is_tainted_by_errors() || inner.region_obligations.is_empty(),
+                "region_obligations not empty: {:#?}",
+                inner.region_obligations
+            );
+            inner
+                .region_constraint_storage
+                .take()
+                .expect("regions already resolved")
+                .with_log(&mut inner.undo_log)
+                .into_infos_and_data()
+        };
+
+        let region_rels =
+            &RegionRelations::new(self.tcx, region_context, outlives_env.free_region_map());
+
+        let (lexical_region_resolutions, errors) =
+            lexical_region_resolve::resolve(region_rels, var_infos, data, mode);
+
+        let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions));
+        assert!(old_value.is_none());
+
+        if !self.is_tainted_by_errors() {
+            // As a heuristic, just skip reporting region errors
+            // altogether if other errors have been reported while
+            // this infcx was in use.  This is totally hokey but
+            // otherwise we have a hard time separating legit region
+            // errors from silly ones.
+            self.report_region_errors(&errors);
+        }
+    }
+
+    /// Obtains (and clears) the current set of region
+    /// constraints. The inference context is still usable: further
+    /// unifications will simply add new constraints.
+    ///
+    /// This method is not meant to be used with normal lexical region
+    /// resolution. Rather, it is used in the NLL mode as a kind of
+    /// interim hack: basically we run normal type-check and generate
+    /// region constraints as normal, but then we take them and
+    /// translate them into the form that the NLL solver
+    /// understands. See the NLL module for mode details.
+    pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> {
+        assert!(
+            self.inner.borrow().region_obligations.is_empty(),
+            "region_obligations not empty: {:#?}",
+            self.inner.borrow().region_obligations
+        );
+
+        self.inner.borrow_mut().unwrap_region_constraints().take_and_reset_data()
+    }
+
+    /// Gives temporary access to the region constraint data.
+    pub fn with_region_constraints<R>(
+        &self,
+        op: impl FnOnce(&RegionConstraintData<'tcx>) -> R,
+    ) -> R {
+        let mut inner = self.inner.borrow_mut();
+        op(inner.unwrap_region_constraints().data())
+    }
+
+    /// Takes ownership of the list of variable regions. This implies
+    /// that all the region constraints have already been taken, and
+    /// hence that `resolve_regions_and_report_errors` can never be
+    /// called. This is used only during NLL processing to "hand off" ownership
+    /// of the set of region variables into the NLL region context.
+    pub fn take_region_var_origins(&self) -> VarInfos {
+        let mut inner = self.inner.borrow_mut();
+        let (var_infos, data) = inner
+            .region_constraint_storage
+            .take()
+            .expect("regions already resolved")
+            .with_log(&mut inner.undo_log)
+            .into_infos_and_data();
+        assert!(data.is_empty());
+        var_infos
+    }
+
+    pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
+        self.resolve_vars_if_possible(t).to_string()
+    }
+
+    /// If `TyVar(vid)` resolves to a type, return that type. Else, return the
+    /// universe index of `TyVar(vid)`.
+    pub fn probe_ty_var(&self, vid: TyVid) -> Result<Ty<'tcx>, ty::UniverseIndex> {
+        use self::type_variable::TypeVariableValue;
+
+        match self.inner.borrow_mut().type_variables().probe(vid) {
+            TypeVariableValue::Known { value } => Ok(value),
+            TypeVariableValue::Unknown { universe } => Err(universe),
+        }
+    }
+
+    /// Resolve any type variables found in `value` -- but only one
+    /// level.  So, if the variable `?X` is bound to some type
+    /// `Foo<?Y>`, then this would return `Foo<?Y>` (but `?Y` may
+    /// itself be bound to a type).
+    ///
+    /// Useful when you only need to inspect the outermost level of
+    /// the type and don't care about nested types (or perhaps you
+    /// will be resolving them as well, e.g. in a loop).
+    pub fn shallow_resolve<T>(&self, value: T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        value.fold_with(&mut ShallowResolver { infcx: self })
+    }
+
+    pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid {
+        self.inner.borrow_mut().type_variables().root_var(var)
+    }
+
+    /// Where possible, replaces type/const variables in
+    /// `value` with their final value. Note that region variables
+    /// are unaffected. If a type/const variable has not been unified, it
+    /// is left as is. This is an idempotent operation that does
+    /// not affect inference state in any way and so you can do it
+    /// at will.
+    pub fn resolve_vars_if_possible<T>(&self, value: T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        if !value.needs_infer() {
+            return value; // Avoid duplicated subst-folding.
+        }
+        let mut r = resolve::OpportunisticVarResolver::new(self);
+        value.fold_with(&mut r)
+    }
+
+    /// Returns the first unresolved variable contained in `T`. In the
+    /// process of visiting `T`, this will resolve (where possible)
+    /// type variables in `T`, but it never constructs the final,
+    /// resolved type, so it's more efficient than
+    /// `resolve_vars_if_possible()`.
+    pub fn unresolved_type_vars<T>(&self, value: &T) -> Option<(Ty<'tcx>, Option<Span>)>
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        value.visit_with(&mut resolve::UnresolvedTypeFinder::new(self)).break_value()
+    }
+
+    pub fn probe_const_var(
+        &self,
+        vid: ty::ConstVid<'tcx>,
+    ) -> Result<&'tcx ty::Const<'tcx>, ty::UniverseIndex> {
+        match self.inner.borrow_mut().const_unification_table().probe_value(vid).val {
+            ConstVariableValue::Known { value } => Ok(value),
+            ConstVariableValue::Unknown { universe } => Err(universe),
+        }
+    }
+
+    pub fn fully_resolve<T: TypeFoldable<'tcx>>(&self, value: T) -> FixupResult<'tcx, T> {
+        /*!
+         * Attempts to resolve all type/region/const variables in
+         * `value`. Region inference must have been run already (e.g.,
+         * by calling `resolve_regions_and_report_errors`). If some
+         * variable was never unified, an `Err` results.
+         *
+         * This method is idempotent, but it not typically not invoked
+         * except during the writeback phase.
+         */
+
+        resolve::fully_resolve(self, value)
+    }
+
+    // [Note-Type-error-reporting]
+    // An invariant is that anytime the expected or actual type is Error (the special
+    // error type, meaning that an error occurred when typechecking this expression),
+    // this is a derived error. The error cascaded from another error (that was already
+    // reported), so it's not useful to display it to the user.
+    // The following methods implement this logic.
+    // They check if either the actual or expected type is Error, and don't print the error
+    // in this case. The typechecker should only ever report type errors involving mismatched
+    // types using one of these methods, and should not call span_err directly for such
+    // errors.
+
+    pub fn type_error_struct_with_diag<M>(
+        &self,
+        sp: Span,
+        mk_diag: M,
+        actual_ty: Ty<'tcx>,
+    ) -> DiagnosticBuilder<'tcx>
+    where
+        M: FnOnce(String) -> DiagnosticBuilder<'tcx>,
+    {
+        let actual_ty = self.resolve_vars_if_possible(actual_ty);
+        debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty);
+
+        // Don't report an error if actual type is `Error`.
+        if actual_ty.references_error() {
+            return self.tcx.sess.diagnostic().struct_dummy();
+        }
+
+        mk_diag(self.ty_to_string(actual_ty))
+    }
+
+    pub fn report_mismatched_types(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        expected: Ty<'tcx>,
+        actual: Ty<'tcx>,
+        err: TypeError<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        let trace = TypeTrace::types(cause, true, expected, actual);
+        self.report_and_explain_type_error(trace, &err)
+    }
+
+    pub fn report_mismatched_consts(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        expected: &'tcx ty::Const<'tcx>,
+        actual: &'tcx ty::Const<'tcx>,
+        err: TypeError<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        let trace = TypeTrace::consts(cause, true, expected, actual);
+        self.report_and_explain_type_error(trace, &err)
+    }
+
+    pub fn replace_bound_vars_with_fresh_vars<T>(
+        &self,
+        span: Span,
+        lbrct: LateBoundRegionConversionTime,
+        value: ty::Binder<'tcx, T>,
+    ) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        let fld_r =
+            |br: ty::BoundRegion| self.next_region_var(LateBoundRegion(span, br.kind, lbrct));
+        let fld_t = |_| {
+            self.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::MiscVariable,
+                span,
+            })
+        };
+        let fld_c = |_, ty| {
+            self.next_const_var(
+                ty,
+                ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span },
+            )
+        };
+        self.tcx.replace_bound_vars(value, fld_r, fld_t, fld_c)
+    }
+
+    /// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method.
+    pub fn verify_generic_bound(
+        &self,
+        origin: SubregionOrigin<'tcx>,
+        kind: GenericKind<'tcx>,
+        a: ty::Region<'tcx>,
+        bound: VerifyBound<'tcx>,
+    ) {
+        debug!("verify_generic_bound({:?}, {:?} <: {:?})", kind, a, bound);
+
+        self.inner
+            .borrow_mut()
+            .unwrap_region_constraints()
+            .verify_generic_bound(origin, kind, a, bound);
+    }
+
+    /// Obtains the latest type of the given closure; this may be a
+    /// closure in the current function, in which case its
+    /// `ClosureKind` may not yet be known.
+    pub fn closure_kind(&self, closure_substs: SubstsRef<'tcx>) -> Option<ty::ClosureKind> {
+        let closure_kind_ty = closure_substs.as_closure().kind_ty();
+        let closure_kind_ty = self.shallow_resolve(closure_kind_ty);
+        closure_kind_ty.to_opt_closure_kind()
+    }
+
+    /// Clears the selection, evaluation, and projection caches. This is useful when
+    /// repeatedly attempting to select an `Obligation` while changing only
+    /// its `ParamEnv`, since `FulfillmentContext` doesn't use probing.
+    pub fn clear_caches(&self) {
+        self.selection_cache.clear();
+        self.evaluation_cache.clear();
+        self.inner.borrow_mut().projection_cache().clear();
+    }
+
+    fn universe(&self) -> ty::UniverseIndex {
+        self.universe.get()
+    }
+
+    /// Creates and return a fresh universe that extends all previous
+    /// universes. Updates `self.universe` to that new universe.
+    pub fn create_next_universe(&self) -> ty::UniverseIndex {
+        let u = self.universe.get().next_universe();
+        self.universe.set(u);
+        u
+    }
+
+    /// Resolves and evaluates a constant.
+    ///
+    /// The constant can be located on a trait like `<A as B>::C`, in which case the given
+    /// substitutions and environment are used to resolve the constant. Alternatively if the
+    /// constant has generic parameters in scope the substitutions are used to evaluate the value of
+    /// the constant. For example in `fn foo<T>() { let _ = [0; bar::<T>()]; }` the repeat count
+    /// constant `bar::<T>()` requires a substitution for `T`, if the substitution for `T` is still
+    /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is
+    /// returned.
+    ///
+    /// This handles inferences variables within both `param_env` and `substs` by
+    /// performing the operation on their respective canonical forms.
+    pub fn const_eval_resolve(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        ty::Unevaluated { def, substs, promoted }: ty::Unevaluated<'tcx>,
+        span: Option<Span>,
+    ) -> EvalToConstValueResult<'tcx> {
+        let mut original_values = OriginalQueryValues::default();
+        let canonical = self.canonicalize_query((param_env, substs), &mut original_values);
+
+        let (param_env, substs) = canonical.value;
+        // The return value is the evaluated value which doesn't contain any reference to inference
+        // variables, thus we don't need to substitute back the original values.
+        self.tcx.const_eval_resolve(param_env, ty::Unevaluated { def, substs, promoted }, span)
+    }
+
+    /// If `typ` is a type variable of some kind, resolve it one level
+    /// (but do not resolve types found in the result). If `typ` is
+    /// not a type variable, just return it unmodified.
+    // FIXME(eddyb) inline into `ShallowResolver::visit_ty`.
+    fn shallow_resolve_ty(&self, typ: Ty<'tcx>) -> Ty<'tcx> {
+        match *typ.kind() {
+            ty::Infer(ty::TyVar(v)) => {
+                // Not entirely obvious: if `typ` is a type variable,
+                // it can be resolved to an int/float variable, which
+                // can then be recursively resolved, hence the
+                // recursion. Note though that we prevent type
+                // variables from unifying to other type variables
+                // directly (though they may be embedded
+                // structurally), and we prevent cycles in any case,
+                // so this recursion should always be of very limited
+                // depth.
+                //
+                // Note: if these two lines are combined into one we get
+                // dynamic borrow errors on `self.inner`.
+                let known = self.inner.borrow_mut().type_variables().probe(v).known();
+                known.map_or(typ, |t| self.shallow_resolve_ty(t))
+            }
+
+            ty::Infer(ty::IntVar(v)) => self
+                .inner
+                .borrow_mut()
+                .int_unification_table()
+                .probe_value(v)
+                .map(|v| v.to_type(self.tcx))
+                .unwrap_or(typ),
+
+            ty::Infer(ty::FloatVar(v)) => self
+                .inner
+                .borrow_mut()
+                .float_unification_table()
+                .probe_value(v)
+                .map(|v| v.to_type(self.tcx))
+                .unwrap_or(typ),
+
+            _ => typ,
+        }
+    }
+
+    /// `ty_or_const_infer_var_changed` is equivalent to one of these two:
+    ///   * `shallow_resolve(ty) != ty` (where `ty.kind = ty::Infer(_)`)
+    ///   * `shallow_resolve(ct) != ct` (where `ct.kind = ty::ConstKind::Infer(_)`)
+    ///
+    /// However, `ty_or_const_infer_var_changed` is more efficient. It's always
+    /// inlined, despite being large, because it has only two call sites that
+    /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on`
+    /// inference variables), and it handles both `Ty` and `ty::Const` without
+    /// having to resort to storing full `GenericArg`s in `stalled_on`.
+    #[inline(always)]
+    pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar<'tcx>) -> bool {
+        match infer_var {
+            TyOrConstInferVar::Ty(v) => {
+                use self::type_variable::TypeVariableValue;
+
+                // If `inlined_probe` returns a `Known` value, it never equals
+                // `ty::Infer(ty::TyVar(v))`.
+                match self.inner.borrow_mut().type_variables().inlined_probe(v) {
+                    TypeVariableValue::Unknown { .. } => false,
+                    TypeVariableValue::Known { .. } => true,
+                }
+            }
+
+            TyOrConstInferVar::TyInt(v) => {
+                // If `inlined_probe_value` returns a value it's always a
+                // `ty::Int(_)` or `ty::UInt(_)`, which never matches a
+                // `ty::Infer(_)`.
+                self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_some()
+            }
+
+            TyOrConstInferVar::TyFloat(v) => {
+                // If `probe_value` returns a value it's always a
+                // `ty::Float(_)`, which never matches a `ty::Infer(_)`.
+                //
+                // Not `inlined_probe_value(v)` because this call site is colder.
+                self.inner.borrow_mut().float_unification_table().probe_value(v).is_some()
+            }
+
+            TyOrConstInferVar::Const(v) => {
+                // If `probe_value` returns a `Known` value, it never equals
+                // `ty::ConstKind::Infer(ty::InferConst::Var(v))`.
+                //
+                // Not `inlined_probe_value(v)` because this call site is colder.
+                match self.inner.borrow_mut().const_unification_table().probe_value(v).val {
+                    ConstVariableValue::Unknown { .. } => false,
+                    ConstVariableValue::Known { .. } => true,
+                }
+            }
+        }
+    }
+}
+
+/// Helper for `ty_or_const_infer_var_changed` (see comment on that), currently
+/// used only for `traits::fulfill`'s list of `stalled_on` inference variables.
+#[derive(Copy, Clone, Debug)]
+pub enum TyOrConstInferVar<'tcx> {
+    /// Equivalent to `ty::Infer(ty::TyVar(_))`.
+    Ty(TyVid),
+    /// Equivalent to `ty::Infer(ty::IntVar(_))`.
+    TyInt(IntVid),
+    /// Equivalent to `ty::Infer(ty::FloatVar(_))`.
+    TyFloat(FloatVid),
+
+    /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`.
+    Const(ConstVid<'tcx>),
+}
+
+impl TyOrConstInferVar<'tcx> {
+    /// Tries to extract an inference variable from a type or a constant, returns `None`
+    /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and
+    /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`).
+    pub fn maybe_from_generic_arg(arg: GenericArg<'tcx>) -> Option<Self> {
+        match arg.unpack() {
+            GenericArgKind::Type(ty) => Self::maybe_from_ty(ty),
+            GenericArgKind::Const(ct) => Self::maybe_from_const(ct),
+            GenericArgKind::Lifetime(_) => None,
+        }
+    }
+
+    /// Tries to extract an inference variable from a type, returns `None`
+    /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`).
+    pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option<Self> {
+        match *ty.kind() {
+            ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)),
+            ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)),
+            ty::Infer(ty::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)),
+            _ => None,
+        }
+    }
+
+    /// Tries to extract an inference variable from a constant, returns `None`
+    /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`).
+    pub fn maybe_from_const(ct: &'tcx ty::Const<'tcx>) -> Option<Self> {
+        match ct.val {
+            ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)),
+            _ => None,
+        }
+    }
+}
+
+struct ShallowResolver<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        self.infcx.shallow_resolve_ty(ty)
+    }
+
+    fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = ct {
+            self.infcx
+                .inner
+                .borrow_mut()
+                .const_unification_table()
+                .probe_value(*vid)
+                .val
+                .known()
+                .unwrap_or(ct)
+        } else {
+            ct
+        }
+    }
+}
+
+impl<'tcx> TypeTrace<'tcx> {
+    pub fn span(&self) -> Span {
+        self.cause.span
+    }
+
+    pub fn types(
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: Ty<'tcx>,
+        b: Ty<'tcx>,
+    ) -> TypeTrace<'tcx> {
+        TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) }
+    }
+
+    pub fn consts(
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) -> TypeTrace<'tcx> {
+        TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) }
+    }
+}
+
+impl<'tcx> SubregionOrigin<'tcx> {
+    pub fn span(&self) -> Span {
+        match *self {
+            Subtype(ref a) => a.span(),
+            RelateObjectBound(a) => a,
+            RelateParamBound(a, ..) => a,
+            RelateRegionParamBound(a) => a,
+            Reborrow(a) => a,
+            ReborrowUpvar(a, _) => a,
+            DataBorrowed(_, a) => a,
+            ReferenceOutlivesReferent(_, a) => a,
+            CallReturn(a) => a,
+            CompareImplMethodObligation { span, .. } => span,
+        }
+    }
+
+    pub fn from_obligation_cause<F>(cause: &traits::ObligationCause<'tcx>, default: F) -> Self
+    where
+        F: FnOnce() -> Self,
+    {
+        match cause.code {
+            traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => {
+                SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span)
+            }
+
+            traits::ObligationCauseCode::CompareImplMethodObligation {
+                item_name,
+                impl_item_def_id,
+                trait_item_def_id,
+            } => SubregionOrigin::CompareImplMethodObligation {
+                span: cause.span,
+                item_name,
+                impl_item_def_id,
+                trait_item_def_id,
+            },
+
+            _ => default(),
+        }
+    }
+}
+
+impl RegionVariableOrigin {
+    pub fn span(&self) -> Span {
+        match *self {
+            MiscVariable(a)
+            | PatternRegion(a)
+            | AddrOfRegion(a)
+            | Autoref(a, _)
+            | Coercion(a)
+            | EarlyBoundRegion(a, ..)
+            | LateBoundRegion(a, ..)
+            | UpvarRegion(_, a) => a,
+            Nll(..) => bug!("NLL variable used with `span`"),
+        }
+    }
+}
+
+impl<'tcx> fmt::Debug for RegionObligation<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "RegionObligation(sub_region={:?}, sup_type={:?})",
+            self.sub_region, self.sup_type
+        )
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
new file mode 100644
index 00000000000..20be06adfd0
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -0,0 +1,1035 @@
+//! This code is kind of an alternate way of doing subtyping,
+//! supertyping, and type equating, distinct from the `combine.rs`
+//! code but very similar in its effect and design. Eventually the two
+//! ought to be merged. This code is intended for use in NLL and chalk.
+//!
+//! Here are the key differences:
+//!
+//! - This code may choose to bypass some checks (e.g., the occurs check)
+//!   in the case where we know that there are no unbound type inference
+//!   variables. This is the case for NLL, because at NLL time types are fully
+//!   inferred up-to regions.
+//! - This code uses "universes" to handle higher-ranked regions and
+//!   not the leak-check. This is "more correct" than what rustc does
+//!   and we are generally migrating in this direction, but NLL had to
+//!   get there first.
+//!
+//! Also, this code assumes that there are no bound types at all, not even
+//! free ones. This is ok because:
+//! - we are not relating anything quantified over some type variable
+//! - we will have instantiated all the bound type vars already (the one
+//!   thing we relate in chalk are basically domain goals and their
+//!   constituents)
+
+use crate::infer::combine::ConstEquateRelation;
+use crate::infer::InferCtxt;
+use crate::infer::{ConstVarValue, ConstVariableValue};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor};
+use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
+use std::fmt::Debug;
+use std::ops::ControlFlow;
+
+#[derive(PartialEq)]
+pub enum NormalizationStrategy {
+    Lazy,
+    Eager,
+}
+
+pub struct TypeRelating<'me, 'tcx, D>
+where
+    D: TypeRelatingDelegate<'tcx>,
+{
+    infcx: &'me InferCtxt<'me, 'tcx>,
+
+    /// Callback to use when we deduce an outlives relationship.
+    delegate: D,
+
+    /// How are we relating `a` and `b`?
+    ///
+    /// - Covariant means `a <: b`.
+    /// - Contravariant means `b <: a`.
+    /// - Invariant means `a == b.
+    /// - Bivariant means that it doesn't matter.
+    ambient_variance: ty::Variance,
+
+    ambient_variance_info: ty::VarianceDiagInfo<'tcx>,
+
+    /// When we pass through a set of binders (e.g., when looking into
+    /// a `fn` type), we push a new bound region scope onto here. This
+    /// will contain the instantiated region for each region in those
+    /// binders. When we then encounter a `ReLateBound(d, br)`, we can
+    /// use the De Bruijn index `d` to find the right scope, and then
+    /// bound region name `br` to find the specific instantiation from
+    /// within that scope. See `replace_bound_region`.
+    ///
+    /// This field stores the instantiations for late-bound regions in
+    /// the `a` type.
+    a_scopes: Vec<BoundRegionScope<'tcx>>,
+
+    /// Same as `a_scopes`, but for the `b` type.
+    b_scopes: Vec<BoundRegionScope<'tcx>>,
+}
+
+pub trait TypeRelatingDelegate<'tcx> {
+    fn param_env(&self) -> ty::ParamEnv<'tcx>;
+
+    /// Push a constraint `sup: sub` -- this constraint must be
+    /// satisfied for the two types to be related. `sub` and `sup` may
+    /// be regions from the type or new variables created through the
+    /// delegate.
+    fn push_outlives(
+        &mut self,
+        sup: ty::Region<'tcx>,
+        sub: ty::Region<'tcx>,
+        info: ty::VarianceDiagInfo<'tcx>,
+    );
+
+    fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
+
+    /// Creates a new universe index. Used when instantiating placeholders.
+    fn create_next_universe(&mut self) -> ty::UniverseIndex;
+
+    /// Creates a new region variable representing a higher-ranked
+    /// region that is instantiated existentially. This creates an
+    /// inference variable, typically.
+    ///
+    /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then
+    /// we will invoke this method to instantiate `'a` with an
+    /// inference variable (though `'b` would be instantiated first,
+    /// as a placeholder).
+    fn next_existential_region_var(&mut self, was_placeholder: bool) -> ty::Region<'tcx>;
+
+    /// Creates a new region variable representing a
+    /// higher-ranked region that is instantiated universally.
+    /// This creates a new region placeholder, typically.
+    ///
+    /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then
+    /// we will invoke this method to instantiate `'b` with a
+    /// placeholder region.
+    fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>;
+
+    /// Creates a new existential region in the given universe. This
+    /// is used when handling subtyping and type variables -- if we
+    /// have that `?X <: Foo<'a>`, for example, we would instantiate
+    /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh
+    /// existential variable created by this function. We would then
+    /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives
+    /// relation stating that `'?0: 'a`).
+    fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
+
+    /// Define the normalization strategy to use, eager or lazy.
+    fn normalization() -> NormalizationStrategy;
+
+    /// Enables some optimizations if we do not expect inference variables
+    /// in the RHS of the relation.
+    fn forbid_inference_vars() -> bool;
+}
+
+#[derive(Clone, Debug, Default)]
+struct BoundRegionScope<'tcx> {
+    map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
+}
+
+#[derive(Copy, Clone)]
+struct UniversallyQuantified(bool);
+
+impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D>
+where
+    D: TypeRelatingDelegate<'tcx>,
+{
+    pub fn new(
+        infcx: &'me InferCtxt<'me, 'tcx>,
+        delegate: D,
+        ambient_variance: ty::Variance,
+    ) -> Self {
+        Self {
+            infcx,
+            delegate,
+            ambient_variance,
+            ambient_variance_info: ty::VarianceDiagInfo::default(),
+            a_scopes: vec![],
+            b_scopes: vec![],
+        }
+    }
+
+    fn ambient_covariance(&self) -> bool {
+        match self.ambient_variance {
+            ty::Variance::Covariant | ty::Variance::Invariant => true,
+            ty::Variance::Contravariant | ty::Variance::Bivariant => false,
+        }
+    }
+
+    fn ambient_contravariance(&self) -> bool {
+        match self.ambient_variance {
+            ty::Variance::Contravariant | ty::Variance::Invariant => true,
+            ty::Variance::Covariant | ty::Variance::Bivariant => false,
+        }
+    }
+
+    fn create_scope(
+        &mut self,
+        value: ty::Binder<'tcx, impl Relate<'tcx>>,
+        universally_quantified: UniversallyQuantified,
+    ) -> BoundRegionScope<'tcx> {
+        let mut scope = BoundRegionScope::default();
+
+        // Create a callback that creates (via the delegate) either an
+        // existential or placeholder region as needed.
+        let mut next_region = {
+            let delegate = &mut self.delegate;
+            let mut lazy_universe = None;
+            move |br: ty::BoundRegion| {
+                if universally_quantified.0 {
+                    // The first time this closure is called, create a
+                    // new universe for the placeholders we will make
+                    // from here out.
+                    let universe = lazy_universe.unwrap_or_else(|| {
+                        let universe = delegate.create_next_universe();
+                        lazy_universe = Some(universe);
+                        universe
+                    });
+
+                    let placeholder = ty::PlaceholderRegion { universe, name: br.kind };
+                    delegate.next_placeholder_region(placeholder)
+                } else {
+                    delegate.next_existential_region_var(true)
+                }
+            }
+        };
+
+        value.skip_binder().visit_with(&mut ScopeInstantiator {
+            next_region: &mut next_region,
+            target_index: ty::INNERMOST,
+            bound_region_scope: &mut scope,
+        });
+
+        scope
+    }
+
+    /// When we encounter binders during the type traversal, we record
+    /// the value to substitute for each of the things contained in
+    /// that binder. (This will be either a universal placeholder or
+    /// an existential inference variable.) Given the De Bruijn index
+    /// `debruijn` (and name `br`) of some binder we have now
+    /// encountered, this routine finds the value that we instantiated
+    /// the region with; to do so, it indexes backwards into the list
+    /// of ambient scopes `scopes`.
+    fn lookup_bound_region(
+        debruijn: ty::DebruijnIndex,
+        br: &ty::BoundRegion,
+        first_free_index: ty::DebruijnIndex,
+        scopes: &[BoundRegionScope<'tcx>],
+    ) -> ty::Region<'tcx> {
+        // The debruijn index is a "reverse index" into the
+        // scopes listing. So when we have INNERMOST (0), we
+        // want the *last* scope pushed, and so forth.
+        let debruijn_index = debruijn.index() - first_free_index.index();
+        let scope = &scopes[scopes.len() - debruijn_index - 1];
+
+        // Find this bound region in that scope to map to a
+        // particular region.
+        scope.map[br]
+    }
+
+    /// If `r` is a bound region, find the scope in which it is bound
+    /// (from `scopes`) and return the value that we instantiated it
+    /// with. Otherwise just return `r`.
+    fn replace_bound_region(
+        &self,
+        r: ty::Region<'tcx>,
+        first_free_index: ty::DebruijnIndex,
+        scopes: &[BoundRegionScope<'tcx>],
+    ) -> ty::Region<'tcx> {
+        debug!("replace_bound_regions(scopes={:?})", scopes);
+        if let ty::ReLateBound(debruijn, br) = r {
+            Self::lookup_bound_region(*debruijn, br, first_free_index, scopes)
+        } else {
+            r
+        }
+    }
+
+    /// Push a new outlives requirement into our output set of
+    /// constraints.
+    fn push_outlives(
+        &mut self,
+        sup: ty::Region<'tcx>,
+        sub: ty::Region<'tcx>,
+        info: ty::VarianceDiagInfo<'tcx>,
+    ) {
+        debug!("push_outlives({:?}: {:?})", sup, sub);
+
+        self.delegate.push_outlives(sup, sub, info);
+    }
+
+    /// Relate a projection type and some value type lazily. This will always
+    /// succeed, but we push an additional `ProjectionEq` goal depending
+    /// on the value type:
+    /// - if the value type is any type `T` which is not a projection, we push
+    ///   `ProjectionEq(projection = T)`.
+    /// - if the value type is another projection `other_projection`, we create
+    ///   a new inference variable `?U` and push the two goals
+    ///   `ProjectionEq(projection = ?U)`, `ProjectionEq(other_projection = ?U)`.
+    fn relate_projection_ty(
+        &mut self,
+        projection_ty: ty::ProjectionTy<'tcx>,
+        value_ty: Ty<'tcx>,
+    ) -> Ty<'tcx> {
+        use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+        use rustc_span::DUMMY_SP;
+
+        match *value_ty.kind() {
+            ty::Projection(other_projection_ty) => {
+                let var = self.infcx.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::MiscVariable,
+                    span: DUMMY_SP,
+                });
+                self.relate_projection_ty(projection_ty, var);
+                self.relate_projection_ty(other_projection_ty, var);
+                var
+            }
+
+            _ => bug!("should never be invoked with eager normalization"),
+        }
+    }
+
+    /// Relate a type inference variable with a value type. This works
+    /// by creating a "generalization" G of the value where all the
+    /// lifetimes are replaced with fresh inference values. This
+    /// generalization G becomes the value of the inference variable,
+    /// and is then related in turn to the value. So e.g. if you had
+    /// `vid = ?0` and `value = &'a u32`, we might first instantiate
+    /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable,
+    /// and then relate `&'0 u32` with `&'a u32` (resulting in
+    /// relations between `'0` and `'a`).
+    ///
+    /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)`
+    /// -- in other words, it is always a (unresolved) inference
+    /// variable `vid` and a type `ty` that are being related, but the
+    /// vid may appear either as the "a" type or the "b" type,
+    /// depending on where it appears in the tuple. The trait
+    /// `VidValuePair` lets us work with the vid/type while preserving
+    /// the "sidedness" when necessary -- the sidedness is relevant in
+    /// particular for the variance and set of in-scope things.
+    fn relate_ty_var<PAIR: VidValuePair<'tcx>>(
+        &mut self,
+        pair: PAIR,
+    ) -> RelateResult<'tcx, Ty<'tcx>> {
+        debug!("relate_ty_var({:?})", pair);
+
+        let vid = pair.vid();
+        let value_ty = pair.value_ty();
+
+        // FIXME(invariance) -- this logic assumes invariance, but that is wrong.
+        // This only presently applies to chalk integration, as NLL
+        // doesn't permit type variables to appear on both sides (and
+        // doesn't use lazy norm).
+        match *value_ty.kind() {
+            ty::Infer(ty::TyVar(value_vid)) => {
+                // Two type variables: just equate them.
+                self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid);
+                return Ok(value_ty);
+            }
+
+            ty::Projection(projection_ty) if D::normalization() == NormalizationStrategy::Lazy => {
+                return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_ty_var(vid)));
+            }
+
+            _ => (),
+        }
+
+        let generalized_ty = self.generalize_value(value_ty, vid)?;
+        debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty);
+
+        if D::forbid_inference_vars() {
+            // In NLL, we don't have type inference variables
+            // floating around, so we can do this rather imprecise
+            // variant of the occurs-check.
+            assert!(!generalized_ty.has_infer_types_or_consts());
+        }
+
+        self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty);
+
+        // The generalized values we extract from `canonical_var_values` have
+        // been fully instantiated and hence the set of scopes we have
+        // doesn't matter -- just to be sure, put an empty vector
+        // in there.
+        let old_a_scopes = std::mem::take(pair.vid_scopes(self));
+
+        // Relate the generalized kind to the original one.
+        let result = pair.relate_generalized_ty(self, generalized_ty);
+
+        // Restore the old scopes now.
+        *pair.vid_scopes(self) = old_a_scopes;
+
+        debug!("relate_ty_var: complete, result = {:?}", result);
+        result
+    }
+
+    fn generalize_value<T: Relate<'tcx>>(
+        &mut self,
+        value: T,
+        for_vid: ty::TyVid,
+    ) -> RelateResult<'tcx, T> {
+        let universe = self.infcx.probe_ty_var(for_vid).unwrap_err();
+
+        let mut generalizer = TypeGeneralizer {
+            infcx: self.infcx,
+            delegate: &mut self.delegate,
+            first_free_index: ty::INNERMOST,
+            ambient_variance: self.ambient_variance,
+            for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid),
+            universe,
+        };
+
+        generalizer.relate(value, value)
+    }
+}
+
+/// When we instantiate a inference variable with a value in
+/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`,
+/// but the ordering may vary (depending on whether the inference
+/// variable was found on the `a` or `b` sides). Therefore, this trait
+/// allows us to factor out common code, while preserving the order
+/// when needed.
+trait VidValuePair<'tcx>: Debug {
+    /// Extract the inference variable (which could be either the
+    /// first or second part of the tuple).
+    fn vid(&self) -> ty::TyVid;
+
+    /// Extract the value it is being related to (which will be the
+    /// opposite part of the tuple from the vid).
+    fn value_ty(&self) -> Ty<'tcx>;
+
+    /// Extract the scopes that apply to whichever side of the tuple
+    /// the vid was found on.  See the comment where this is called
+    /// for more details on why we want them.
+    fn vid_scopes<D: TypeRelatingDelegate<'tcx>>(
+        &self,
+        relate: &'r mut TypeRelating<'_, 'tcx, D>,
+    ) -> &'r mut Vec<BoundRegionScope<'tcx>>;
+
+    /// Given a generalized type G that should replace the vid, relate
+    /// G to the value, putting G on whichever side the vid would have
+    /// appeared.
+    fn relate_generalized_ty<D>(
+        &self,
+        relate: &mut TypeRelating<'_, 'tcx, D>,
+        generalized_ty: Ty<'tcx>,
+    ) -> RelateResult<'tcx, Ty<'tcx>>
+    where
+        D: TypeRelatingDelegate<'tcx>;
+}
+
+impl VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) {
+    fn vid(&self) -> ty::TyVid {
+        self.0
+    }
+
+    fn value_ty(&self) -> Ty<'tcx> {
+        self.1
+    }
+
+    fn vid_scopes<D>(
+        &self,
+        relate: &'r mut TypeRelating<'_, 'tcx, D>,
+    ) -> &'r mut Vec<BoundRegionScope<'tcx>>
+    where
+        D: TypeRelatingDelegate<'tcx>,
+    {
+        &mut relate.a_scopes
+    }
+
+    fn relate_generalized_ty<D>(
+        &self,
+        relate: &mut TypeRelating<'_, 'tcx, D>,
+        generalized_ty: Ty<'tcx>,
+    ) -> RelateResult<'tcx, Ty<'tcx>>
+    where
+        D: TypeRelatingDelegate<'tcx>,
+    {
+        relate.relate(&generalized_ty, &self.value_ty())
+    }
+}
+
+// In this case, the "vid" is the "b" type.
+impl VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) {
+    fn vid(&self) -> ty::TyVid {
+        self.1
+    }
+
+    fn value_ty(&self) -> Ty<'tcx> {
+        self.0
+    }
+
+    fn vid_scopes<D>(
+        &self,
+        relate: &'r mut TypeRelating<'_, 'tcx, D>,
+    ) -> &'r mut Vec<BoundRegionScope<'tcx>>
+    where
+        D: TypeRelatingDelegate<'tcx>,
+    {
+        &mut relate.b_scopes
+    }
+
+    fn relate_generalized_ty<D>(
+        &self,
+        relate: &mut TypeRelating<'_, 'tcx, D>,
+        generalized_ty: Ty<'tcx>,
+    ) -> RelateResult<'tcx, Ty<'tcx>>
+    where
+        D: TypeRelatingDelegate<'tcx>,
+    {
+        relate.relate(&self.value_ty(), &generalized_ty)
+    }
+}
+
+impl<D> TypeRelation<'tcx> for TypeRelating<'me, 'tcx, D>
+where
+    D: TypeRelatingDelegate<'tcx>,
+{
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.delegate.param_env()
+    }
+
+    fn tag(&self) -> &'static str {
+        "nll::subtype"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        debug!("relate_with_variance(variance={:?}, a={:?}, b={:?})", variance, a, b);
+
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+        self.ambient_variance_info = self.ambient_variance_info.clone().xform(info);
+
+        debug!("relate_with_variance: ambient_variance = {:?}", self.ambient_variance);
+
+        let r = self.relate(a, b)?;
+
+        self.ambient_variance = old_ambient_variance;
+
+        debug!("relate_with_variance: r={:?}", r);
+
+        Ok(r)
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        let a = self.infcx.shallow_resolve(a);
+
+        if !D::forbid_inference_vars() {
+            b = self.infcx.shallow_resolve(b);
+        }
+
+        if a == b {
+            // Subtle: if a or b has a bound variable that we are lazilly
+            // substituting, then even if a == b, it could be that the values we
+            // will substitute for those bound variables are *not* the same, and
+            // hence returning `Ok(a)` is incorrect.
+            if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
+                return Ok(a);
+            }
+        }
+
+        match (a.kind(), b.kind()) {
+            (_, &ty::Infer(ty::TyVar(vid))) => {
+                if D::forbid_inference_vars() {
+                    // Forbid inference variables in the RHS.
+                    bug!("unexpected inference var {:?}", b)
+                } else {
+                    self.relate_ty_var((a, vid))
+                }
+            }
+
+            (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)),
+
+            (&ty::Projection(projection_ty), _)
+                if D::normalization() == NormalizationStrategy::Lazy =>
+            {
+                Ok(self.relate_projection_ty(projection_ty, b))
+            }
+
+            (_, &ty::Projection(projection_ty))
+                if D::normalization() == NormalizationStrategy::Lazy =>
+            {
+                Ok(self.relate_projection_ty(projection_ty, a))
+            }
+
+            _ => {
+                debug!("tys(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance);
+
+                // Will also handle unification of `IntVar` and `FloatVar`.
+                self.infcx.super_combine_tys(self, a, b)
+            }
+        }
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug!("regions(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance);
+
+        let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes);
+        let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes);
+
+        debug!("regions: v_a = {:?}", v_a);
+        debug!("regions: v_b = {:?}", v_b);
+
+        if self.ambient_covariance() {
+            // Covariance: a <= b. Hence, `b: a`.
+            self.push_outlives(v_b, v_a, self.ambient_variance_info.clone());
+        }
+
+        if self.ambient_contravariance() {
+            // Contravariant: b <= a. Hence, `a: b`.
+            self.push_outlives(v_a, v_b, self.ambient_variance_info.clone());
+        }
+
+        Ok(a)
+    }
+
+    fn consts(
+        &mut self,
+        a: &'tcx ty::Const<'tcx>,
+        mut b: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        let a = self.infcx.shallow_resolve(a);
+
+        if !D::forbid_inference_vars() {
+            b = self.infcx.shallow_resolve(b);
+        }
+
+        match b.val {
+            ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
+                // Forbid inference variables in the RHS.
+                bug!("unexpected inference var {:?}", b)
+            }
+            // FIXME(invariance): see the related FIXME above.
+            _ => self.infcx.super_combine_consts(self, a, b),
+        }
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        // We want that
+        //
+        // ```
+        // for<'a> fn(&'a u32) -> &'a u32 <:
+        //   fn(&'b u32) -> &'b u32
+        // ```
+        //
+        // but not
+        //
+        // ```
+        // fn(&'a u32) -> &'a u32 <:
+        //   for<'b> fn(&'b u32) -> &'b u32
+        // ```
+        //
+        // We therefore proceed as follows:
+        //
+        // - Instantiate binders on `b` universally, yielding a universe U1.
+        // - Instantiate binders on `a` existentially in U1.
+
+        debug!("binders({:?}: {:?}, ambient_variance={:?})", a, b, self.ambient_variance);
+
+        if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) {
+            // Fast path for the common case.
+            self.relate(a, b)?;
+            return Ok(ty::Binder::dummy(a));
+        }
+
+        if self.ambient_covariance() {
+            // Covariance, so we want `for<..> A <: for<..> B` --
+            // therefore we compare any instantiation of A (i.e., A
+            // instantiated with existentials) against every
+            // instantiation of B (i.e., B instantiated with
+            // universals).
+
+            let b_scope = self.create_scope(b, UniversallyQuantified(true));
+            let a_scope = self.create_scope(a, UniversallyQuantified(false));
+
+            debug!("binders: a_scope = {:?} (existential)", a_scope);
+            debug!("binders: b_scope = {:?} (universal)", b_scope);
+
+            self.b_scopes.push(b_scope);
+            self.a_scopes.push(a_scope);
+
+            // Reset the ambient variance to covariant. This is needed
+            // to correctly handle cases like
+            //
+            //     for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32)
+            //
+            // Somewhat surprisingly, these two types are actually
+            // **equal**, even though the one on the right looks more
+            // polymorphic. The reason is due to subtyping. To see it,
+            // consider that each function can call the other:
+            //
+            // - The left function can call the right with `'b` and
+            //   `'c` both equal to `'a`
+            //
+            // - The right function can call the left with `'a` set to
+            //   `{P}`, where P is the point in the CFG where the call
+            //   itself occurs. Note that `'b` and `'c` must both
+            //   include P. At the point, the call works because of
+            //   subtyping (i.e., `&'b u32 <: &{P} u32`).
+            let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant);
+
+            self.relate(a.skip_binder(), b.skip_binder())?;
+
+            self.ambient_variance = variance;
+
+            self.b_scopes.pop().unwrap();
+            self.a_scopes.pop().unwrap();
+        }
+
+        if self.ambient_contravariance() {
+            // Contravariance, so we want `for<..> A :> for<..> B`
+            // -- therefore we compare every instantiation of A (i.e.,
+            // A instantiated with universals) against any
+            // instantiation of B (i.e., B instantiated with
+            // existentials). Opposite of above.
+
+            let a_scope = self.create_scope(a, UniversallyQuantified(true));
+            let b_scope = self.create_scope(b, UniversallyQuantified(false));
+
+            debug!("binders: a_scope = {:?} (universal)", a_scope);
+            debug!("binders: b_scope = {:?} (existential)", b_scope);
+
+            self.a_scopes.push(a_scope);
+            self.b_scopes.push(b_scope);
+
+            // Reset ambient variance to contravariance. See the
+            // covariant case above for an explanation.
+            let variance =
+                std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);
+
+            self.relate(a.skip_binder(), b.skip_binder())?;
+
+            self.ambient_variance = variance;
+
+            self.b_scopes.pop().unwrap();
+            self.a_scopes.pop().unwrap();
+        }
+
+        Ok(a)
+    }
+}
+
+impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D>
+where
+    D: TypeRelatingDelegate<'tcx>,
+{
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.delegate.const_equate(a, b);
+    }
+}
+
+/// When we encounter a binder like `for<..> fn(..)`, we actually have
+/// to walk the `fn` value to find all the values bound by the `for`
+/// (these are not explicitly present in the ty representation right
+/// now). This visitor handles that: it descends the type, tracking
+/// binder depth, and finds late-bound regions targeting the
+/// `for<..`>.  For each of those, it creates an entry in
+/// `bound_region_scope`.
+struct ScopeInstantiator<'me, 'tcx> {
+    next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
+    // The debruijn index of the scope we are instantiating.
+    target_index: ty::DebruijnIndex,
+    bound_region_scope: &'me mut BoundRegionScope<'tcx>,
+}
+
+impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> {
+    fn visit_binder<T: TypeFoldable<'tcx>>(
+        &mut self,
+        t: &ty::Binder<'tcx, T>,
+    ) -> ControlFlow<Self::BreakTy> {
+        self.target_index.shift_in(1);
+        t.super_visit_with(self);
+        self.target_index.shift_out(1);
+
+        ControlFlow::CONTINUE
+    }
+
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        let ScopeInstantiator { bound_region_scope, next_region, .. } = self;
+
+        match r {
+            ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => {
+                bound_region_scope.map.entry(*br).or_insert_with(|| next_region(*br));
+            }
+
+            _ => {}
+        }
+
+        ControlFlow::CONTINUE
+    }
+}
+
+/// The "type generalizer" is used when handling inference variables.
+///
+/// The basic strategy for handling a constraint like `?A <: B` is to
+/// apply a "generalization strategy" to the type `B` -- this replaces
+/// all the lifetimes in the type `B` with fresh inference
+/// variables. (You can read more about the strategy in this [blog
+/// post].)
+///
+/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x
+/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the
+/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which
+/// establishes `'0: 'x` as a constraint.
+///
+/// As a side-effect of this generalization procedure, we also replace
+/// all the bound regions that we have traversed with concrete values,
+/// so that the resulting generalized type is independent from the
+/// scopes.
+///
+/// [blog post]: https://is.gd/0hKvIr
+struct TypeGeneralizer<'me, 'tcx, D>
+where
+    D: TypeRelatingDelegate<'tcx>,
+{
+    infcx: &'me InferCtxt<'me, 'tcx>,
+
+    delegate: &'me mut D,
+
+    /// After we generalize this type, we are going to relative it to
+    /// some other type. What will be the variance at this point?
+    ambient_variance: ty::Variance,
+
+    first_free_index: ty::DebruijnIndex,
+
+    /// The vid of the type variable that is in the process of being
+    /// instantiated. If we find this within the value we are folding,
+    /// that means we would have created a cyclic value.
+    for_vid_sub_root: ty::TyVid,
+
+    /// The universe of the type variable that is in the process of being
+    /// instantiated. If we find anything that this universe cannot name,
+    /// we reject the relation.
+    universe: ty::UniverseIndex,
+}
+
+impl<D> TypeRelation<'tcx> for TypeGeneralizer<'me, 'tcx, D>
+where
+    D: TypeRelatingDelegate<'tcx>,
+{
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.delegate.param_env()
+    }
+
+    fn tag(&self) -> &'static str {
+        "nll::generalizer"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        debug!(
+            "TypeGeneralizer::relate_with_variance(variance={:?}, a={:?}, b={:?})",
+            variance, a, b
+        );
+
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+
+        debug!(
+            "TypeGeneralizer::relate_with_variance: ambient_variance = {:?}",
+            self.ambient_variance
+        );
+
+        let r = self.relate(a, b)?;
+
+        self.ambient_variance = old_ambient_variance;
+
+        debug!("TypeGeneralizer::relate_with_variance: r={:?}", r);
+
+        Ok(r)
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        use crate::infer::type_variable::TypeVariableValue;
+
+        debug!("TypeGeneralizer::tys(a={:?})", a);
+
+        match *a.kind() {
+            ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_))
+                if D::forbid_inference_vars() =>
+            {
+                bug!("unexpected inference variable encountered in NLL generalization: {:?}", a);
+            }
+
+            ty::Infer(ty::TyVar(vid)) => {
+                let mut inner = self.infcx.inner.borrow_mut();
+                let variables = &mut inner.type_variables();
+                let vid = variables.root_var(vid);
+                let sub_vid = variables.sub_root_var(vid);
+                if sub_vid == self.for_vid_sub_root {
+                    // If sub-roots are equal, then `for_vid` and
+                    // `vid` are related via subtyping.
+                    debug!("TypeGeneralizer::tys: occurs check failed");
+                    Err(TypeError::Mismatch)
+                } else {
+                    match variables.probe(vid) {
+                        TypeVariableValue::Known { value: u } => {
+                            drop(variables);
+                            self.relate(u, u)
+                        }
+                        TypeVariableValue::Unknown { universe: _universe } => {
+                            if self.ambient_variance == ty::Bivariant {
+                                // FIXME: we may need a WF predicate (related to #54105).
+                            }
+
+                            let origin = *variables.var_origin(vid);
+
+                            // Replacing with a new variable in the universe `self.universe`,
+                            // it will be unified later with the original type variable in
+                            // the universe `_universe`.
+                            let new_var_id = variables.new_var(self.universe, false, origin);
+
+                            let u = self.tcx().mk_ty_var(new_var_id);
+                            debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);
+                            Ok(u)
+                        }
+                    }
+                }
+            }
+
+            ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
+                // No matter what mode we are in,
+                // integer/floating-point types must be equal to be
+                // relatable.
+                Ok(a)
+            }
+
+            ty::Placeholder(placeholder) => {
+                if self.universe.cannot_name(placeholder.universe) {
+                    debug!(
+                        "TypeGeneralizer::tys: root universe {:?} cannot name\
+                         placeholder in universe {:?}",
+                        self.universe, placeholder.universe
+                    );
+                    Err(TypeError::Mismatch)
+                } else {
+                    Ok(a)
+                }
+            }
+
+            _ => relate::super_relate_tys(self, a, a),
+        }
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        _: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug!("TypeGeneralizer::regions(a={:?})", a);
+
+        if let ty::ReLateBound(debruijn, _) = a {
+            if *debruijn < self.first_free_index {
+                return Ok(a);
+            }
+        }
+
+        // For now, we just always create a fresh region variable to
+        // replace all the regions in the source type. In the main
+        // type checker, we special case the case where the ambient
+        // variance is `Invariant` and try to avoid creating a fresh
+        // region variable, but since this comes up so much less in
+        // NLL (only when users use `_` etc) it is much less
+        // important.
+        //
+        // As an aside, since these new variables are created in
+        // `self.universe` universe, this also serves to enforce the
+        // universe scoping rules.
+        //
+        // FIXME(#54105) -- if the ambient variance is bivariant,
+        // though, we may however need to check well-formedness or
+        // risk a problem like #41677 again.
+
+        let replacement_region_vid = self.delegate.generalize_existential(self.universe);
+
+        Ok(replacement_region_vid)
+    }
+
+    fn consts(
+        &mut self,
+        a: &'tcx ty::Const<'tcx>,
+        _: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        match a.val {
+            ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
+                bug!("unexpected inference variable encountered in NLL generalization: {:?}", a);
+            }
+            ty::ConstKind::Infer(InferConst::Var(vid)) => {
+                let mut inner = self.infcx.inner.borrow_mut();
+                let variable_table = &mut inner.const_unification_table();
+                let var_value = variable_table.probe_value(vid);
+                match var_value.val.known() {
+                    Some(u) => self.relate(u, u),
+                    None => {
+                        let new_var_id = variable_table.new_key(ConstVarValue {
+                            origin: var_value.origin,
+                            val: ConstVariableValue::Unknown { universe: self.universe },
+                        });
+                        Ok(self.tcx().mk_const_var(new_var_id, a.ty))
+                    }
+                }
+            }
+            ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a),
+            _ => relate::super_relate_consts(self, a, a),
+        }
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        _: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        debug!("TypeGeneralizer::binders(a={:?})", a);
+
+        self.first_free_index.shift_in(1);
+        let result = self.relate(a.skip_binder(), a.skip_binder())?;
+        self.first_free_index.shift_out(1);
+        Ok(a.rebind(result))
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs
new file mode 100644
index 00000000000..9e04773c5fa
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -0,0 +1,195 @@
+use crate::infer::free_regions::FreeRegionMap;
+use crate::infer::{GenericKind, InferCtxt};
+use crate::traits::query::OutlivesBound;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_middle::ty;
+
+use super::explicit_outlives_bounds;
+
+/// The `OutlivesEnvironment` collects information about what outlives
+/// what in a given type-checking setting. For example, if we have a
+/// where-clause like `where T: 'a` in scope, then the
+/// `OutlivesEnvironment` would record that (in its
+/// `region_bound_pairs` field). Similarly, it contains methods for
+/// processing and adding implied bounds into the outlives
+/// environment.
+///
+/// Other code at present does not typically take a
+/// `&OutlivesEnvironment`, but rather takes some of its fields (e.g.,
+/// `process_registered_region_obligations` wants the
+/// region-bound-pairs). There is no mistaking it: the current setup
+/// of tracking region information is quite scattered! The
+/// `OutlivesEnvironment`, for example, needs to sometimes be combined
+/// with the `middle::RegionRelations`, to yield a full picture of how
+/// (lexical) lifetimes interact. However, I'm reluctant to do more
+/// refactoring here, since the setup with NLL is quite different.
+/// For example, NLL has no need of `RegionRelations`, and is solely
+/// interested in the `OutlivesEnvironment`. -nmatsakis
+#[derive(Clone)]
+pub struct OutlivesEnvironment<'tcx> {
+    pub param_env: ty::ParamEnv<'tcx>,
+    free_region_map: FreeRegionMap<'tcx>,
+
+    // Contains, for each body B that we are checking (that is, the fn
+    // item, but also any nested closures), the set of implied region
+    // bounds that are in scope in that particular body.
+    //
+    // Example:
+    //
+    // ```
+    // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) {
+    //   bar(x, y, |y: &'b T| { .. } // body B1)
+    // } // body B0
+    // ```
+    //
+    // Here, for body B0, the list would be `[T: 'a]`, because we
+    // infer that `T` must outlive `'a` from the implied bounds on the
+    // fn declaration.
+    //
+    // For the body B1, the list would be `[T: 'a, T: 'b]`, because we
+    // also can see that -- within the closure body! -- `T` must
+    // outlive `'b`. This is not necessarily true outside the closure
+    // body, since the closure may never be called.
+    //
+    // We collect this map as we descend the tree. We then use the
+    // results when proving outlives obligations like `T: 'x` later
+    // (e.g., if `T: 'x` must be proven within the body B1, then we
+    // know it is true if either `'a: 'x` or `'b: 'x`).
+    region_bound_pairs_map: FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>,
+
+    // Used to compute `region_bound_pairs_map`: contains the set of
+    // in-scope region-bound pairs thus far.
+    region_bound_pairs_accum: RegionBoundPairs<'tcx>,
+}
+
+/// "Region-bound pairs" tracks outlives relations that are known to
+/// be true, either because of explicit where-clauses like `T: 'a` or
+/// because of implied bounds.
+pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>;
+
+impl<'a, 'tcx> OutlivesEnvironment<'tcx> {
+    pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
+        let mut env = OutlivesEnvironment {
+            param_env,
+            free_region_map: Default::default(),
+            region_bound_pairs_map: Default::default(),
+            region_bound_pairs_accum: vec![],
+        };
+
+        env.add_outlives_bounds(None, explicit_outlives_bounds(param_env));
+
+        env
+    }
+
+    /// Borrows current value of the `free_region_map`.
+    pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> {
+        &self.free_region_map
+    }
+
+    /// Borrows current value of the `region_bound_pairs`.
+    pub fn region_bound_pairs_map(&self) -> &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>> {
+        &self.region_bound_pairs_map
+    }
+
+    /// This is a hack to support the old-skool regionck, which
+    /// processes region constraints from the main function and the
+    /// closure together. In that context, when we enter a closure, we
+    /// want to be able to "save" the state of the surrounding a
+    /// function. We can then add implied bounds and the like from the
+    /// closure arguments into the environment -- these should only
+    /// apply in the closure body, so once we exit, we invoke
+    /// `pop_snapshot_post_closure` to remove them.
+    ///
+    /// Example:
+    ///
+    /// ```
+    /// fn foo<T>() {
+    ///    callback(for<'a> |x: &'a T| {
+    ///         // ^^^^^^^ not legal syntax, but probably should be
+    ///         // within this closure body, `T: 'a` holds
+    ///    })
+    /// }
+    /// ```
+    ///
+    /// This "containment" of closure's effects only works so well. In
+    /// particular, we (intentionally) leak relationships between free
+    /// regions that are created by the closure's bounds. The case
+    /// where this is useful is when you have (e.g.) a closure with a
+    /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this
+    /// case, we want to keep the relationship `'b: 'a` in the
+    /// free-region-map, so that later if we have to take `LUB('b,
+    /// 'a)` we can get the result `'b`.
+    ///
+    /// I have opted to keep **all modifications** to the
+    /// free-region-map, however, and not just those that concern free
+    /// variables bound in the closure. The latter seems more correct,
+    /// but it is not the existing behavior, and I could not find a
+    /// case where the existing behavior went wrong. In any case, it
+    /// seems like it'd be readily fixed if we wanted. There are
+    /// similar leaks around givens that seem equally suspicious, to
+    /// be honest. --nmatsakis
+    pub fn push_snapshot_pre_closure(&self) -> usize {
+        self.region_bound_pairs_accum.len()
+    }
+
+    /// See `push_snapshot_pre_closure`.
+    pub fn pop_snapshot_post_closure(&mut self, len: usize) {
+        self.region_bound_pairs_accum.truncate(len);
+    }
+
+    /// Save the current set of region-bound pairs under the given `body_id`.
+    pub fn save_implied_bounds(&mut self, body_id: hir::HirId) {
+        let old =
+            self.region_bound_pairs_map.insert(body_id, self.region_bound_pairs_accum.clone());
+        assert!(old.is_none());
+    }
+
+    /// Processes outlives bounds that are known to hold, whether from implied or other sources.
+    ///
+    /// The `infcx` parameter is optional; if the implied bounds may
+    /// contain inference variables, it must be supplied, in which
+    /// case we will register "givens" on the inference context. (See
+    /// `RegionConstraintData`.)
+    pub fn add_outlives_bounds<I>(
+        &mut self,
+        infcx: Option<&InferCtxt<'a, 'tcx>>,
+        outlives_bounds: I,
+    ) where
+        I: IntoIterator<Item = OutlivesBound<'tcx>>,
+    {
+        // Record relationships such as `T:'x` that don't go into the
+        // free-region-map but which we use here.
+        for outlives_bound in outlives_bounds {
+            debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
+            match outlives_bound {
+                OutlivesBound::RegionSubRegion(
+                    r_a @ (&ty::ReEarlyBound(_) | &ty::ReFree(_)),
+                    &ty::ReVar(vid_b),
+                ) => {
+                    infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b);
+                }
+                OutlivesBound::RegionSubParam(r_a, param_b) => {
+                    self.region_bound_pairs_accum.push((r_a, GenericKind::Param(param_b)));
+                }
+                OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+                    self.region_bound_pairs_accum
+                        .push((r_a, GenericKind::Projection(projection_b)));
+                }
+                OutlivesBound::RegionSubRegion(r_a, r_b) => {
+                    // In principle, we could record (and take
+                    // advantage of) every relationship here, but
+                    // we are also free not to -- it simply means
+                    // strictly less that we can successfully type
+                    // check. Right now we only look for things
+                    // relationships between free regions. (It may
+                    // also be that we should revise our inference
+                    // system to be more general and to make use
+                    // of *every* relationship that arises here,
+                    // but presently we do not.)
+                    self.free_region_map.relate_regions(r_a, r_b);
+                }
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
new file mode 100644
index 00000000000..07c75d50d91
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -0,0 +1,34 @@
+//! Various code related to computing outlives relations.
+
+pub mod env;
+pub mod obligations;
+pub mod verify;
+
+use rustc_middle::traits::query::OutlivesBound;
+use rustc_middle::ty;
+
+pub fn explicit_outlives_bounds<'tcx>(
+    param_env: ty::ParamEnv<'tcx>,
+) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
+    debug!("explicit_outlives_bounds()");
+    param_env
+        .caller_bounds()
+        .into_iter()
+        .map(ty::Predicate::kind)
+        .filter_map(ty::Binder::no_bound_vars)
+        .filter_map(move |kind| match kind {
+            ty::PredicateKind::Projection(..)
+            | ty::PredicateKind::Trait(..)
+            | ty::PredicateKind::Subtype(..)
+            | ty::PredicateKind::WellFormed(..)
+            | ty::PredicateKind::ObjectSafe(..)
+            | ty::PredicateKind::ClosureKind(..)
+            | ty::PredicateKind::TypeOutlives(..)
+            | ty::PredicateKind::ConstEvaluatable(..)
+            | ty::PredicateKind::ConstEquate(..)
+            | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
+            ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
+                Some(OutlivesBound::RegionSubRegion(r_b, r_a))
+            }
+        })
+}
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
new file mode 100644
index 00000000000..437083c68dc
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -0,0 +1,468 @@
+//! Code that handles "type-outlives" constraints like `T: 'a`. This
+//! is based on the `push_outlives_components` function defined on the tcx,
+//! but it adds a bit of heuristics on top, in particular to deal with
+//! associated types and projections.
+//!
+//! When we process a given `T: 'a` obligation, we may produce two
+//! kinds of constraints for the region inferencer:
+//!
+//! - Relationships between inference variables and other regions.
+//!   For example, if we have `&'?0 u32: 'a`, then we would produce
+//!   a constraint that `'a <= '?0`.
+//! - "Verifys" that must be checked after inferencing is done.
+//!   For example, if we know that, for some type parameter `T`,
+//!   `T: 'a + 'b`, and we have a requirement that `T: '?1`,
+//!   then we add a "verify" that checks that `'?1 <= 'a || '?1 <= 'b`.
+//!   - Note the difference with the previous case: here, the region
+//!     variable must be less than something else, so this doesn't
+//!     affect how inference works (it finds the smallest region that
+//!     will do); it's just a post-condition that we have to check.
+//!
+//! **The key point is that once this function is done, we have
+//! reduced all of our "type-region outlives" obligations into relationships
+//! between individual regions.**
+//!
+//! One key input to this function is the set of "region-bound pairs".
+//! These are basically the relationships between type parameters and
+//! regions that are in scope at the point where the outlives
+//! obligation was incurred. **When type-checking a function,
+//! particularly in the face of closures, this is not known until
+//! regionck runs!** This is because some of those bounds come
+//! from things we have yet to infer.
+//!
+//! Consider:
+//!
+//! ```
+//! fn bar<T>(a: T, b: impl for<'a> Fn(&'a T));
+//! fn foo<T>(x: T) {
+//!     bar(x, |y| { ... })
+//!          // ^ closure arg
+//! }
+//! ```
+//!
+//! Here, the type of `y` may involve inference variables and the
+//! like, and it may also contain implied bounds that are needed to
+//! type-check the closure body (e.g., here it informs us that `T`
+//! outlives the late-bound region `'a`).
+//!
+//! Note that by delaying the gathering of implied bounds until all
+//! inference information is known, we may find relationships between
+//! bound regions and other regions in the environment. For example,
+//! when we first check a closure like the one expected as argument
+//! to `foo`:
+//!
+//! ```
+//! fn foo<U, F: for<'a> FnMut(&'a U)>(_f: F) {}
+//! ```
+//!
+//! the type of the closure's first argument would be `&'a ?U`. We
+//! might later infer `?U` to something like `&'b u32`, which would
+//! imply that `'b: 'a`.
+
+use crate::infer::outlives::env::RegionBoundPairs;
+use crate::infer::outlives::verify::VerifyBoundCx;
+use crate::infer::{
+    self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, UndoLog, VerifyBound,
+};
+use crate::traits::{ObligationCause, ObligationCauseCode};
+use rustc_middle::ty::outlives::Component;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeFoldable};
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::undo_log::UndoLogs;
+use rustc_hir as hir;
+use smallvec::smallvec;
+
+impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
+    /// Registers that the given region obligation must be resolved
+    /// from within the scope of `body_id`. These regions are enqueued
+    /// and later processed by regionck, when full type information is
+    /// available (see `region_obligations` field for more
+    /// information).
+    pub fn register_region_obligation(
+        &self,
+        body_id: hir::HirId,
+        obligation: RegionObligation<'tcx>,
+    ) {
+        debug!("register_region_obligation(body_id={:?}, obligation={:?})", body_id, obligation);
+
+        let mut inner = self.inner.borrow_mut();
+        inner.undo_log.push(UndoLog::PushRegionObligation);
+        inner.region_obligations.push((body_id, obligation));
+    }
+
+    pub fn register_region_obligation_with_cause(
+        &self,
+        sup_type: Ty<'tcx>,
+        sub_region: Region<'tcx>,
+        cause: &ObligationCause<'tcx>,
+    ) {
+        let origin = SubregionOrigin::from_obligation_cause(cause, || {
+            infer::RelateParamBound(
+                cause.span,
+                sup_type,
+                match cause.code.peel_derives() {
+                    ObligationCauseCode::BindingObligation(_, span) => Some(*span),
+                    _ => None,
+                },
+            )
+        });
+
+        self.register_region_obligation(
+            cause.body_id,
+            RegionObligation { sup_type, sub_region, origin },
+        );
+    }
+
+    /// Trait queries just want to pass back type obligations "as is"
+    pub fn take_registered_region_obligations(&self) -> Vec<(hir::HirId, RegionObligation<'tcx>)> {
+        std::mem::take(&mut self.inner.borrow_mut().region_obligations)
+    }
+
+    /// Process the region obligations that must be proven (during
+    /// `regionck`) for the given `body_id`, given information about
+    /// the region bounds in scope and so forth. This function must be
+    /// invoked for all relevant body-ids before region inference is
+    /// done (or else an assert will fire).
+    ///
+    /// See the `region_obligations` field of `InferCtxt` for some
+    /// comments about how this function fits into the overall expected
+    /// flow of the inferencer. The key point is that it is
+    /// invoked after all type-inference variables have been bound --
+    /// towards the end of regionck. This also ensures that the
+    /// region-bound-pairs are available (see comments above regarding
+    /// closures).
+    ///
+    /// # Parameters
+    ///
+    /// - `region_bound_pairs`: the set of region bounds implied by
+    ///   the parameters and where-clauses. In particular, each pair
+    ///   `('a, K)` in this list tells us that the bounds in scope
+    ///   indicate that `K: 'a`, where `K` is either a generic
+    ///   parameter like `T` or a projection like `T::Item`.
+    /// - `implicit_region_bound`: if some, this is a region bound
+    ///   that is considered to hold for all type parameters (the
+    ///   function body).
+    /// - `param_env` is the parameter environment for the enclosing function.
+    /// - `body_id` is the body-id whose region obligations are being
+    ///   processed.
+    ///
+    /// # Returns
+    ///
+    /// This function may have to perform normalizations, and hence it
+    /// returns an `InferOk` with subobligations that must be
+    /// processed.
+    pub fn process_registered_region_obligations(
+        &self,
+        region_bound_pairs_map: &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>,
+        implicit_region_bound: Option<ty::Region<'tcx>>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) {
+        assert!(
+            !self.in_snapshot.get(),
+            "cannot process registered region obligations in a snapshot"
+        );
+
+        debug!("process_registered_region_obligations()");
+
+        let my_region_obligations = self.take_registered_region_obligations();
+
+        for (body_id, RegionObligation { sup_type, sub_region, origin }) in my_region_obligations {
+            debug!(
+                "process_registered_region_obligations: sup_type={:?} sub_region={:?} origin={:?}",
+                sup_type, sub_region, origin
+            );
+
+            let sup_type = self.resolve_vars_if_possible(sup_type);
+
+            if let Some(region_bound_pairs) = region_bound_pairs_map.get(&body_id) {
+                let outlives = &mut TypeOutlives::new(
+                    self,
+                    self.tcx,
+                    &region_bound_pairs,
+                    implicit_region_bound,
+                    param_env,
+                );
+                outlives.type_must_outlive(origin, sup_type, sub_region);
+            } else {
+                self.tcx.sess.delay_span_bug(
+                    origin.span(),
+                    &format!("no region-bound-pairs for {:?}", body_id),
+                )
+            }
+        }
+    }
+}
+
+/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
+/// obligation into a series of `'a: 'b` constraints and "verify"s, as
+/// described on the module comment. The final constraints are emitted
+/// via a "delegate" of type `D` -- this is usually the `infcx`, which
+/// accrues them into the `region_obligations` code, but for NLL we
+/// use something else.
+pub struct TypeOutlives<'cx, 'tcx, D>
+where
+    D: TypeOutlivesDelegate<'tcx>,
+{
+    // See the comments on `process_registered_region_obligations` for the meaning
+    // of these fields.
+    delegate: D,
+    tcx: TyCtxt<'tcx>,
+    verify_bound: VerifyBoundCx<'cx, 'tcx>,
+}
+
+pub trait TypeOutlivesDelegate<'tcx> {
+    fn push_sub_region_constraint(
+        &mut self,
+        origin: SubregionOrigin<'tcx>,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    );
+
+    fn push_verify(
+        &mut self,
+        origin: SubregionOrigin<'tcx>,
+        kind: GenericKind<'tcx>,
+        a: ty::Region<'tcx>,
+        bound: VerifyBound<'tcx>,
+    );
+}
+
+impl<'cx, 'tcx, D> TypeOutlives<'cx, 'tcx, D>
+where
+    D: TypeOutlivesDelegate<'tcx>,
+{
+    pub fn new(
+        delegate: D,
+        tcx: TyCtxt<'tcx>,
+        region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
+        implicit_region_bound: Option<ty::Region<'tcx>>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Self {
+        Self {
+            delegate,
+            tcx,
+            verify_bound: VerifyBoundCx::new(
+                tcx,
+                region_bound_pairs,
+                implicit_region_bound,
+                param_env,
+            ),
+        }
+    }
+
+    /// Adds constraints to inference such that `T: 'a` holds (or
+    /// reports an error if it cannot).
+    ///
+    /// # Parameters
+    ///
+    /// - `origin`, the reason we need this constraint
+    /// - `ty`, the type `T`
+    /// - `region`, the region `'a`
+    pub fn type_must_outlive(
+        &mut self,
+        origin: infer::SubregionOrigin<'tcx>,
+        ty: Ty<'tcx>,
+        region: ty::Region<'tcx>,
+    ) {
+        debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", ty, region, origin);
+
+        assert!(!ty.has_escaping_bound_vars());
+
+        let mut components = smallvec![];
+        self.tcx.push_outlives_components(ty, &mut components);
+        self.components_must_outlive(origin, &components, region);
+    }
+
+    fn components_must_outlive(
+        &mut self,
+        origin: infer::SubregionOrigin<'tcx>,
+        components: &[Component<'tcx>],
+        region: ty::Region<'tcx>,
+    ) {
+        for component in components.iter() {
+            let origin = origin.clone();
+            match component {
+                Component::Region(region1) => {
+                    self.delegate.push_sub_region_constraint(origin, region, region1);
+                }
+                Component::Param(param_ty) => {
+                    self.param_ty_must_outlive(origin, region, *param_ty);
+                }
+                Component::Projection(projection_ty) => {
+                    self.projection_must_outlive(origin, region, *projection_ty);
+                }
+                Component::EscapingProjection(subcomponents) => {
+                    self.components_must_outlive(origin, &subcomponents, region);
+                }
+                Component::UnresolvedInferenceVariable(v) => {
+                    // ignore this, we presume it will yield an error
+                    // later, since if a type variable is not resolved by
+                    // this point it never will be
+                    self.tcx.sess.delay_span_bug(
+                        origin.span(),
+                        &format!("unresolved inference variable in outlives: {:?}", v),
+                    );
+                }
+            }
+        }
+    }
+
+    fn param_ty_must_outlive(
+        &mut self,
+        origin: infer::SubregionOrigin<'tcx>,
+        region: ty::Region<'tcx>,
+        param_ty: ty::ParamTy,
+    ) {
+        debug!(
+            "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})",
+            region, param_ty, origin
+        );
+
+        let generic = GenericKind::Param(param_ty);
+        let verify_bound = self.verify_bound.generic_bound(generic);
+        self.delegate.push_verify(origin, generic, region, verify_bound);
+    }
+
+    fn projection_must_outlive(
+        &mut self,
+        origin: infer::SubregionOrigin<'tcx>,
+        region: ty::Region<'tcx>,
+        projection_ty: ty::ProjectionTy<'tcx>,
+    ) {
+        debug!(
+            "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})",
+            region, projection_ty, origin
+        );
+
+        // This case is thorny for inference. The fundamental problem is
+        // that there are many cases where we have choice, and inference
+        // doesn't like choice (the current region inference in
+        // particular). :) First off, we have to choose between using the
+        // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and
+        // OutlivesProjectionComponent rules, any one of which is
+        // sufficient.  If there are no inference variables involved, it's
+        // not hard to pick the right rule, but if there are, we're in a
+        // bit of a catch 22: if we picked which rule we were going to
+        // use, we could add constraints to the region inference graph
+        // that make it apply, but if we don't add those constraints, the
+        // rule might not apply (but another rule might). For now, we err
+        // on the side of adding too few edges into the graph.
+
+        // Compute the bounds we can derive from the trait definition.
+        // These are guaranteed to apply, no matter the inference
+        // results.
+        let trait_bounds: Vec<_> =
+            self.verify_bound.projection_declared_bounds_from_trait(projection_ty).collect();
+
+        // Compute the bounds we can derive from the environment. This
+        // is an "approximate" match -- in some cases, these bounds
+        // may not apply.
+        let mut approx_env_bounds =
+            self.verify_bound.projection_approx_declared_bounds_from_env(projection_ty);
+        debug!("projection_must_outlive: approx_env_bounds={:?}", approx_env_bounds);
+
+        // Remove outlives bounds that we get from the environment but
+        // which are also deducable from the trait. This arises (cc
+        // #55756) in cases where you have e.g., `<T as Foo<'a>>::Item:
+        // 'a` in the environment but `trait Foo<'b> { type Item: 'b
+        // }` in the trait definition.
+        approx_env_bounds.retain(|bound| match *bound.0.kind() {
+            ty::Projection(projection_ty) => self
+                .verify_bound
+                .projection_declared_bounds_from_trait(projection_ty)
+                .all(|r| r != bound.1),
+
+            _ => panic!("expected only projection types from env, not {:?}", bound.0),
+        });
+
+        // If declared bounds list is empty, the only applicable rule is
+        // OutlivesProjectionComponent. If there are inference variables,
+        // then, we can break down the outlives into more primitive
+        // components without adding unnecessary edges.
+        //
+        // If there are *no* inference variables, however, we COULD do
+        // this, but we choose not to, because the error messages are less
+        // good. For example, a requirement like `T::Item: 'r` would be
+        // translated to a requirement that `T: 'r`; when this is reported
+        // to the user, it will thus say "T: 'r must hold so that T::Item:
+        // 'r holds". But that makes it sound like the only way to fix
+        // the problem is to add `T: 'r`, which isn't true. So, if there are no
+        // inference variables, we use a verify constraint instead of adding
+        // edges, which winds up enforcing the same condition.
+        let needs_infer = projection_ty.needs_infer();
+        if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer {
+            debug!("projection_must_outlive: no declared bounds");
+
+            for k in projection_ty.substs {
+                match k.unpack() {
+                    GenericArgKind::Lifetime(lt) => {
+                        self.delegate.push_sub_region_constraint(origin.clone(), region, lt);
+                    }
+                    GenericArgKind::Type(ty) => {
+                        self.type_must_outlive(origin.clone(), ty, region);
+                    }
+                    GenericArgKind::Const(_) => {
+                        // Const parameters don't impose constraints.
+                    }
+                }
+            }
+
+            return;
+        }
+
+        // If we found a unique bound `'b` from the trait, and we
+        // found nothing else from the environment, then the best
+        // action is to require that `'b: 'r`, so do that.
+        //
+        // This is best no matter what rule we use:
+        //
+        // - OutlivesProjectionEnv: these would translate to the requirement that `'b:'r`
+        // - OutlivesProjectionTraitDef: these would translate to the requirement that `'b:'r`
+        // - OutlivesProjectionComponent: this would require `'b:'r`
+        //   in addition to other conditions
+        if !trait_bounds.is_empty()
+            && trait_bounds[1..]
+                .iter()
+                .chain(approx_env_bounds.iter().map(|b| &b.1))
+                .all(|b| *b == trait_bounds[0])
+        {
+            let unique_bound = trait_bounds[0];
+            debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound);
+            debug!("projection_must_outlive: unique declared bound appears in trait ref");
+            self.delegate.push_sub_region_constraint(origin, region, unique_bound);
+            return;
+        }
+
+        // Fallback to verifying after the fact that there exists a
+        // declared bound, or that all the components appearing in the
+        // projection outlive; in some cases, this may add insufficient
+        // edges into the inference graph, leading to inference failures
+        // even though a satisfactory solution exists.
+        let generic = GenericKind::Projection(projection_ty);
+        let verify_bound = self.verify_bound.generic_bound(generic);
+        self.delegate.push_verify(origin, generic, region, verify_bound);
+    }
+}
+
+impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'tcx> {
+    fn push_sub_region_constraint(
+        &mut self,
+        origin: SubregionOrigin<'tcx>,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) {
+        self.sub_regions(origin, a, b)
+    }
+
+    fn push_verify(
+        &mut self,
+        origin: SubregionOrigin<'tcx>,
+        kind: GenericKind<'tcx>,
+        a: ty::Region<'tcx>,
+        bound: VerifyBound<'tcx>,
+    ) {
+        self.verify_generic_bound(origin, kind, a, bound)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
new file mode 100644
index 00000000000..f69212c599b
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -0,0 +1,355 @@
+use crate::infer::outlives::env::RegionBoundPairs;
+use crate::infer::{GenericKind, VerifyBound};
+use rustc_data_structures::captures::Captures;
+use rustc_data_structures::sso::SsoHashSet;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
+/// obligation into a series of `'a: 'b` constraints and "verifys", as
+/// described on the module comment. The final constraints are emitted
+/// via a "delegate" of type `D` -- this is usually the `infcx`, which
+/// accrues them into the `region_obligations` code, but for NLL we
+/// use something else.
+pub struct VerifyBoundCx<'cx, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
+    implicit_region_bound: Option<ty::Region<'tcx>>,
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
+        implicit_region_bound: Option<ty::Region<'tcx>>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Self {
+        Self { tcx, region_bound_pairs, implicit_region_bound, param_env }
+    }
+
+    /// Returns a "verify bound" that encodes what we know about
+    /// `generic` and the regions it outlives.
+    pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> {
+        let mut visited = SsoHashSet::new();
+        match generic {
+            GenericKind::Param(param_ty) => self.param_bound(param_ty),
+            GenericKind::Projection(projection_ty) => {
+                self.projection_bound(projection_ty, &mut visited)
+            }
+        }
+    }
+
+    fn type_bound(
+        &self,
+        ty: Ty<'tcx>,
+        visited: &mut SsoHashSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
+        match *ty.kind() {
+            ty::Param(p) => self.param_bound(p),
+            ty::Projection(data) => self.projection_bound(data, visited),
+            ty::FnDef(_, substs) => {
+                // HACK(eddyb) ignore lifetimes found shallowly in `substs`.
+                // This is inconsistent with `ty::Adt` (including all substs),
+                // but consistent with previous (accidental) behavior.
+                // See https://github.com/rust-lang/rust/issues/70917
+                // for further background and discussion.
+                let mut bounds = substs
+                    .iter()
+                    .filter_map(|child| match child.unpack() {
+                        GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
+                        GenericArgKind::Lifetime(_) => None,
+                        GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
+                    })
+                    .filter(|bound| {
+                        // Remove bounds that must hold, since they are not interesting.
+                        !bound.must_hold()
+                    });
+
+                match (bounds.next(), bounds.next()) {
+                    (Some(first), None) => first,
+                    (first, second) => VerifyBound::AllBounds(
+                        first.into_iter().chain(second).chain(bounds).collect(),
+                    ),
+                }
+            }
+            _ => self.recursive_bound(ty.into(), visited),
+        }
+    }
+
+    fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
+        debug!("param_bound(param_ty={:?})", param_ty);
+
+        // Start with anything like `T: 'a` we can scrape from the
+        // environment
+        let param_bounds = self
+            .declared_generic_bounds_from_env(GenericKind::Param(param_ty))
+            .into_iter()
+            .map(|outlives| outlives.1);
+
+        // Add in the default bound of fn body that applies to all in
+        // scope type parameters:
+        let param_bounds = param_bounds.chain(self.implicit_region_bound);
+
+        let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect();
+
+        if any_bounds.is_empty() {
+            // We know that all types `T` outlive `'empty`, so if we
+            // can find no other bound, then check that the region
+            // being tested is `'empty`.
+            VerifyBound::IsEmpty
+        } else {
+            // If we can find any other bound `R` such that `T: R`, then
+            // we don't need to check for `'empty`, because `R: 'empty`.
+            VerifyBound::AnyBound(any_bounds)
+        }
+    }
+
+    /// Given a projection like `T::Item`, searches the environment
+    /// for where-clauses like `T::Item: 'a`. Returns the set of
+    /// regions `'a` that it finds.
+    ///
+    /// This is an "approximate" check -- it may not find all
+    /// applicable bounds, and not all the bounds it returns can be
+    /// relied upon. In particular, this check ignores region
+    /// identity. So, for example, if we have `<T as
+    /// Trait<'0>>::Item` where `'0` is a region variable, and the
+    /// user has `<T as Trait<'a>>::Item: 'b` in the environment, then
+    /// the clause from the environment only applies if `'0 = 'a`,
+    /// which we don't know yet. But we would still include `'b` in
+    /// this list.
+    pub fn projection_approx_declared_bounds_from_env(
+        &self,
+        projection_ty: ty::ProjectionTy<'tcx>,
+    ) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
+        let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx);
+        let erased_projection_ty = self.tcx.erase_regions(projection_ty);
+        self.declared_generic_bounds_from_env_with_compare_fn(|ty| {
+            if let ty::Projection(..) = ty.kind() {
+                let erased_ty = self.tcx.erase_regions(ty);
+                erased_ty == erased_projection_ty
+            } else {
+                false
+            }
+        })
+    }
+
+    /// Searches the where-clauses in scope for regions that
+    /// `projection_ty` is known to outlive. Currently requires an
+    /// exact match.
+    pub fn projection_declared_bounds_from_trait(
+        &self,
+        projection_ty: ty::ProjectionTy<'tcx>,
+    ) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> {
+        self.declared_projection_bounds_from_trait(projection_ty)
+    }
+
+    pub fn projection_bound(
+        &self,
+        projection_ty: ty::ProjectionTy<'tcx>,
+        visited: &mut SsoHashSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
+        debug!("projection_bound(projection_ty={:?})", projection_ty);
+
+        let projection_ty_as_ty =
+            self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
+
+        // Search the env for where clauses like `P: 'a`.
+        let env_bounds = self
+            .projection_approx_declared_bounds_from_env(projection_ty)
+            .into_iter()
+            .map(|ty::OutlivesPredicate(ty, r)| {
+                let vb = VerifyBound::OutlivedBy(r);
+                if ty == projection_ty_as_ty {
+                    // Micro-optimize if this is an exact match (this
+                    // occurs often when there are no region variables
+                    // involved).
+                    vb
+                } else {
+                    VerifyBound::IfEq(ty, Box::new(vb))
+                }
+            });
+
+        // Extend with bounds that we can find from the trait.
+        let trait_bounds = self
+            .projection_declared_bounds_from_trait(projection_ty)
+            .map(|r| VerifyBound::OutlivedBy(r));
+
+        // see the extensive comment in projection_must_outlive
+        let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
+        let recursive_bound = self.recursive_bound(ty.into(), visited);
+
+        VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
+    }
+
+    fn recursive_bound(
+        &self,
+        parent: GenericArg<'tcx>,
+        visited: &mut SsoHashSet<GenericArg<'tcx>>,
+    ) -> VerifyBound<'tcx> {
+        let mut bounds = parent
+            .walk_shallow(visited)
+            .filter_map(|child| match child.unpack() {
+                GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
+                GenericArgKind::Lifetime(lt) => {
+                    // Ignore late-bound regions.
+                    if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None }
+                }
+                GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
+            })
+            .filter(|bound| {
+                // Remove bounds that must hold, since they are not interesting.
+                !bound.must_hold()
+            });
+
+        match (bounds.next(), bounds.next()) {
+            (Some(first), None) => first,
+            (first, second) => {
+                VerifyBound::AllBounds(first.into_iter().chain(second).chain(bounds).collect())
+            }
+        }
+    }
+
+    /// Searches the environment for where-clauses like `G: 'a` where
+    /// `G` is either some type parameter `T` or a projection like
+    /// `T::Item`. Returns a vector of the `'a` bounds it can find.
+    ///
+    /// This is a conservative check -- it may not find all applicable
+    /// bounds, but all the bounds it returns can be relied upon.
+    fn declared_generic_bounds_from_env(
+        &self,
+        generic: GenericKind<'tcx>,
+    ) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
+        let generic_ty = generic.to_ty(self.tcx);
+        self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty)
+    }
+
+    fn declared_generic_bounds_from_env_with_compare_fn(
+        &self,
+        compare_ty: impl Fn(Ty<'tcx>) -> bool,
+    ) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
+        let tcx = self.tcx;
+
+        // To start, collect bounds from user environment. Note that
+        // parameter environments are already elaborated, so we don't
+        // have to worry about that. Comparing using `==` is a bit
+        // dubious for projections, but it will work for simple cases
+        // like `T` and `T::Item`. It may not work as well for things
+        // like `<T as Foo<'a>>::Item`.
+        let c_b = self.param_env.caller_bounds();
+        let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b.into_iter());
+
+        // Next, collect regions we scraped from the well-formedness
+        // constraints in the fn signature. To do that, we walk the list
+        // of known relations from the fn ctxt.
+        //
+        // This is crucial because otherwise code like this fails:
+        //
+        //     fn foo<'a, A>(x: &'a A) { x.bar() }
+        //
+        // The problem is that the type of `x` is `&'a A`. To be
+        // well-formed, then, A must be lower-generic by `'a`, but we
+        // don't know that this holds from first principles.
+        let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| {
+            debug!(
+                "declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}",
+                (r, p)
+            );
+            let p_ty = p.to_ty(tcx);
+            compare_ty(p_ty).then_some(ty::OutlivesPredicate(p_ty, r))
+        });
+
+        param_bounds
+            .chain(from_region_bound_pairs)
+            .inspect(|bound| {
+                debug!(
+                    "declared_generic_bounds_from_env_with_compare_fn: result predicate = {:?}",
+                    bound
+                )
+            })
+            .collect()
+    }
+
+    /// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
+    /// declared in the trait definition. For example, if the trait were
+    ///
+    /// ```rust
+    /// trait Foo<'a> {
+    ///     type Bar: 'a;
+    /// }
+    /// ```
+    ///
+    /// then this function would return `'x`. This is subject to the
+    /// limitations around higher-ranked bounds described in
+    /// `region_bounds_declared_on_associated_item`.
+    fn declared_projection_bounds_from_trait(
+        &self,
+        projection_ty: ty::ProjectionTy<'tcx>,
+    ) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> {
+        debug!("projection_bounds(projection_ty={:?})", projection_ty);
+        let tcx = self.tcx;
+        self.region_bounds_declared_on_associated_item(projection_ty.item_def_id)
+            .map(move |r| r.subst(tcx, projection_ty.substs))
+    }
+
+    /// Given the `DefId` of an associated item, returns any region
+    /// bounds attached to that associated item from the trait definition.
+    ///
+    /// For example:
+    ///
+    /// ```rust
+    /// trait Foo<'a> {
+    ///     type Bar: 'a;
+    /// }
+    /// ```
+    ///
+    /// If we were given the `DefId` of `Foo::Bar`, we would return
+    /// `'a`. You could then apply the substitutions from the
+    /// projection to convert this into your namespace. This also
+    /// works if the user writes `where <Self as Foo<'a>>::Bar: 'a` on
+    /// the trait. In fact, it works by searching for just such a
+    /// where-clause.
+    ///
+    /// It will not, however, work for higher-ranked bounds like:
+    ///
+    /// ```rust
+    /// trait Foo<'a, 'b>
+    /// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
+    /// {
+    ///     type Bar;
+    /// }
+    /// ```
+    ///
+    /// This is for simplicity, and because we are not really smart
+    /// enough to cope with such bounds anywhere.
+    fn region_bounds_declared_on_associated_item(
+        &self,
+        assoc_item_def_id: DefId,
+    ) -> impl Iterator<Item = ty::Region<'tcx>> {
+        let tcx = self.tcx;
+        let bounds = tcx.item_bounds(assoc_item_def_id);
+        bounds
+            .into_iter()
+            .filter_map(|p| p.to_opt_type_outlives())
+            .filter_map(|p| p.no_bound_vars())
+            .map(|b| b.1)
+    }
+
+    /// Searches through a predicate list for a predicate `T: 'a`.
+    ///
+    /// Careful: does not elaborate predicates, and just uses `==`
+    /// when comparing `ty` for equality, so `ty` must be something
+    /// that does not involve inference variables and where you
+    /// otherwise want a precise match.
+    fn collect_outlives_from_predicate_list(
+        &self,
+        compare_ty: impl Fn(Ty<'tcx>) -> bool,
+        predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
+    ) -> impl Iterator<Item = ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
+        predicates
+            .filter_map(|p| p.to_opt_type_outlives())
+            .filter_map(|p| p.no_bound_vars())
+            .filter(move |p| compare_ty(p.0))
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/region_constraints/README.md b/compiler/rustc_infer/src/infer/region_constraints/README.md
new file mode 100644
index 00000000000..0231dd06677
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/region_constraints/README.md
@@ -0,0 +1,3 @@
+For info on how the current borrowck works, see the [rustc dev guide].
+
+[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
new file mode 100644
index 00000000000..2d4c1e5d050
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
@@ -0,0 +1,446 @@
+use super::*;
+use crate::infer::CombinedSnapshot;
+use rustc_data_structures::{
+    graph::{scc::Sccs, vec_graph::VecGraph},
+    undo_log::UndoLogs,
+};
+use rustc_index::vec::Idx;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::relate::RelateResult;
+
+impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
+    /// Searches new universes created during `snapshot`, looking for
+    /// placeholders that may "leak" out from the universes they are contained
+    /// in. If any leaking placeholders are found, then an `Err` is returned
+    /// (typically leading to the snapshot being reversed).
+    ///
+    /// The leak check *used* to be the only way we had to handle higher-ranked
+    /// obligations. Now that we have integrated universes into the region
+    /// solvers, this is no longer the case, but we retain the leak check for
+    /// backwards compatibility purposes. In particular, it lets us make "early"
+    /// decisions about whether a region error will be reported that are used in
+    /// coherence and elsewhere -- see #56105 and #59490 for more details. The
+    /// eventual fate of the leak checker is not yet settled.
+    ///
+    /// The leak checker works by searching for the following error patterns:
+    ///
+    /// * P1: P2, where P1 != P2
+    /// * P1: R, where R is in some universe that cannot name P1
+    ///
+    /// The idea here is that each of these patterns represents something that
+    /// the region solver would eventually report as an error, so we can detect
+    /// the error early. There is a fly in the ointment, though, in that this is
+    /// not entirely true. In particular, in the future, we may extend the
+    /// environment with implied bounds or other info about how placeholders
+    /// relate to regions in outer universes. In that case, `P1: R` for example
+    /// might become solveable.
+    ///
+    /// # Summary of the implementation
+    ///
+    /// The leak checks as follows. First, we construct a graph where `R2: R1`
+    /// implies `R2 -> R1`, and we compute the SCCs.
+    ///
+    /// For each SCC S, we compute:
+    ///
+    /// * what placeholder P it must be equal to, if any
+    ///   * if there are multiple placeholders that must be equal, report an error because `P1: P2`
+    /// * the minimum universe of its constituents
+    ///
+    /// Then we walk the SCCs in dependency order and compute
+    ///
+    /// * what placeholder they must outlive transitively
+    ///   * if they must also be equal to a placeholder, report an error because `P1: P2`
+    /// * minimum universe U of all SCCs they must outlive
+    ///   * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
+    ///     indicates `P: R` and `R` is in an incompatible universe
+    ///
+    /// # Historical note
+    ///
+    /// Older variants of the leak check used to report errors for these
+    /// patterns, but we no longer do:
+    ///
+    /// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
+    /// * R: P1, R: P2, as above
+    pub fn leak_check(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        overly_polymorphic: bool,
+        max_universe: ty::UniverseIndex,
+        snapshot: &CombinedSnapshot<'_, 'tcx>,
+    ) -> RelateResult<'tcx, ()> {
+        debug!(
+            "leak_check(max_universe={:?}, snapshot.universe={:?}, overly_polymorphic={:?})",
+            max_universe, snapshot.universe, overly_polymorphic
+        );
+
+        assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
+
+        let universe_at_start_of_snapshot = snapshot.universe;
+        if universe_at_start_of_snapshot == max_universe {
+            return Ok(());
+        }
+
+        let mini_graph =
+            &MiniGraph::new(tcx, self.undo_log.region_constraints(), &self.storage.data.verifys);
+
+        let mut leak_check = LeakCheck::new(
+            tcx,
+            universe_at_start_of_snapshot,
+            max_universe,
+            overly_polymorphic,
+            mini_graph,
+            self,
+        );
+        leak_check.assign_placeholder_values()?;
+        leak_check.propagate_scc_value()?;
+        Ok(())
+    }
+}
+
+struct LeakCheck<'me, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    universe_at_start_of_snapshot: ty::UniverseIndex,
+    overly_polymorphic: bool,
+    mini_graph: &'me MiniGraph<'tcx>,
+    rcc: &'me RegionConstraintCollector<'me, 'tcx>,
+
+    // Initially, for each SCC S, stores a placeholder `P` such that `S = P`
+    // must hold.
+    //
+    // Later, during the [`LeakCheck::propagate_scc_value`] function, this array
+    // is repurposed to store some placeholder `P` such that the weaker
+    // condition `S: P` must hold. (This is true if `S: S1` transitively and `S1
+    // = P`.)
+    scc_placeholders: IndexVec<LeakCheckScc, Option<ty::PlaceholderRegion>>,
+
+    // For each SCC S, track the minimum universe that flows into it. Note that
+    // this is both the minimum of the universes for every region that is a
+    // member of the SCC, but also if you have `R1: R2`, then the universe of
+    // `R2` must be less than the universe of `R1` (i.e., `R1` flows `R2`). To
+    // see that, imagine that you have `P1: R` -- in that case, `R` must be
+    // either the placeholder `P1` or the empty region in that same universe.
+    //
+    // To detect errors, we look for an SCC S where the values in
+    // `scc_values[S]` (if any) cannot be stored into `scc_universes[S]`.
+    scc_universes: IndexVec<LeakCheckScc, SccUniverse<'tcx>>,
+}
+
+impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
+    fn new(
+        tcx: TyCtxt<'tcx>,
+        universe_at_start_of_snapshot: ty::UniverseIndex,
+        max_universe: ty::UniverseIndex,
+        overly_polymorphic: bool,
+        mini_graph: &'me MiniGraph<'tcx>,
+        rcc: &'me RegionConstraintCollector<'me, 'tcx>,
+    ) -> Self {
+        let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
+        Self {
+            tcx,
+            universe_at_start_of_snapshot,
+            overly_polymorphic,
+            mini_graph,
+            rcc,
+            scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
+            scc_universes: IndexVec::from_elem_n(dummy_scc_universe, mini_graph.sccs.num_sccs()),
+        }
+    }
+
+    /// Compute what placeholders (if any) each SCC must be equal to.
+    /// Also compute the minimum universe of all the regions in each SCC.
+    fn assign_placeholder_values(&mut self) -> RelateResult<'tcx, ()> {
+        // First walk: find each placeholder that is from a newly created universe.
+        for (region, leak_check_node) in &self.mini_graph.nodes {
+            let scc = self.mini_graph.sccs.scc(*leak_check_node);
+
+            // Set the universe of each SCC to be the minimum of its constituent universes
+            let universe = self.rcc.universe(region);
+            debug!(
+                "assign_placeholder_values: scc={:?} universe={:?} region={:?}",
+                scc, universe, region
+            );
+            self.scc_universes[scc].take_min(universe, region);
+
+            // Detect those SCCs that directly contain a placeholder
+            if let ty::RePlaceholder(placeholder) = region {
+                if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) {
+                    self.assign_scc_value(scc, *placeholder)?;
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    // assign_scc_value(S, P): Update `scc_values` to account for the fact that `P: S` must hold.
+    // This may create an error.
+    fn assign_scc_value(
+        &mut self,
+        scc: LeakCheckScc,
+        placeholder: ty::PlaceholderRegion,
+    ) -> RelateResult<'tcx, ()> {
+        match self.scc_placeholders[scc] {
+            Some(p) => {
+                assert_ne!(p, placeholder);
+                return Err(self.placeholder_error(p, placeholder));
+            }
+            None => {
+                self.scc_placeholders[scc] = Some(placeholder);
+            }
+        };
+
+        Ok(())
+    }
+
+    /// For each SCC S, iterate over each successor S1 where `S: S1`:
+    ///
+    /// * Compute
+    /// Iterate over each SCC `S` and ensure that, for each `S1` where `S1: S`,
+    /// `universe(S) <= universe(S1)`. This executes after
+    /// `assign_placeholder_values`, so `universe(S)` is already the minimum
+    /// universe of any of its direct constituents.
+    fn propagate_scc_value(&mut self) -> RelateResult<'tcx, ()> {
+        // Loop invariants:
+        //
+        // On start of the loop iteration for `scc1`:
+        //
+        // * `scc_universes[scc1]` contains the minimum universe of the
+        //   constituents of `scc1`
+        // * `scc_placeholder[scc1]` stores the placeholder that `scc1` must
+        //   be equal to (if any)
+        //
+        // For each succssor `scc2` where `scc1: scc2`:
+        //
+        // * `scc_placeholder[scc2]` stores some placeholder `P` where
+        //   `scc2: P` (if any)
+        // * `scc_universes[scc2]` contains the minimum universe of the
+        //   constituents of `scc2` and any of its successors
+        for scc1 in self.mini_graph.sccs.all_sccs() {
+            debug!(
+                "propagate_scc_value: scc={:?} with universe {:?}",
+                scc1, self.scc_universes[scc1]
+            );
+
+            // Walk over each `scc2` such that `scc1: scc2` and compute:
+            //
+            // * `scc1_universe`: the minimum universe of `scc2` and the constituents of `scc1`
+            // * `succ_bound`: placeholder `P` that the successors must outlive, if any (if there are multiple,
+            //   we pick one arbitrarily)
+            let mut scc1_universe = self.scc_universes[scc1];
+            let mut succ_bound = None;
+            for &scc2 in self.mini_graph.sccs.successors(scc1) {
+                let SccUniverse { universe: scc2_universe, region: scc2_region } =
+                    self.scc_universes[scc2];
+
+                scc1_universe.take_min(scc2_universe, scc2_region.unwrap());
+
+                if let Some(b) = self.scc_placeholders[scc2] {
+                    succ_bound = Some(b);
+                }
+            }
+
+            // Update minimum universe of scc1.
+            self.scc_universes[scc1] = scc1_universe;
+
+            // At this point, `scc_placholder[scc1]` stores the placeholder that
+            // `scc1` must be equal to, if any.
+            if let Some(scc1_placeholder) = self.scc_placeholders[scc1] {
+                debug!(
+                    "propagate_scc_value: scc1={:?} placeholder={:?} scc1_universe={:?}",
+                    scc1, scc1_placeholder, scc1_universe
+                );
+
+                // Check if `P1: R` for some `R` in a universe that cannot name
+                // P1. That's an error.
+                if scc1_universe.universe.cannot_name(scc1_placeholder.universe) {
+                    return Err(self.error(scc1_placeholder, scc1_universe.region.unwrap()));
+                }
+
+                // Check if we have some placeholder where `S: P2`
+                // (transitively). In that case, since `S = P1`, that implies
+                // `P1: P2`, which is an error condition.
+                if let Some(scc2_placeholder) = succ_bound {
+                    assert_ne!(scc1_placeholder, scc2_placeholder);
+                    return Err(self.placeholder_error(scc1_placeholder, scc2_placeholder));
+                }
+            } else {
+                // Otherwise, we can reach a placeholder if some successor can.
+                self.scc_placeholders[scc1] = succ_bound;
+            }
+
+            // At this point, `scc_placeholder[scc1]` stores some placeholder that `scc1` must outlive (if any).
+        }
+        Ok(())
+    }
+
+    fn placeholder_error(
+        &self,
+        placeholder1: ty::PlaceholderRegion,
+        placeholder2: ty::PlaceholderRegion,
+    ) -> TypeError<'tcx> {
+        self.error(placeholder1, self.tcx.mk_region(ty::RePlaceholder(placeholder2)))
+    }
+
+    fn error(
+        &self,
+        placeholder: ty::PlaceholderRegion,
+        other_region: ty::Region<'tcx>,
+    ) -> TypeError<'tcx> {
+        debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
+        if self.overly_polymorphic {
+            TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region)
+        } else {
+            TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region)
+        }
+    }
+}
+
+// States we need to distinguish:
+//
+// * must be equal to a placeholder (i.e., a placeholder is in the SCC)
+//     * it could conflict with some other regions in the SCC in different universes
+//     * or a different placeholder
+// * `P1: S` and `S` must be equal to a placeholder
+// * `P1: S` and `S` is in an incompatible universe
+//
+// So if we
+//
+// (a) compute which placeholder (if any) each SCC must be equal to
+// (b) compute its minimum universe
+// (c) compute *some* placeholder where `S: P1` (any one will do)
+//
+// then we get an error if:
+//
+// - it must be equal to a placeholder `P1` and minimum universe cannot name `P1`
+// - `S: P1` and minimum universe cannot name `P1`
+// - `S: P1` and we must be equal to `P2`
+//
+// So we want to track:
+//
+// * Equal placeholder (if any)
+// * Some bounding placeholder (if any)
+// * Minimum universe
+//
+// * We compute equal placeholder + minimum universe of constituents in first pass
+// * Then we walk in order and compute from our dependencies `S1` where `S: S1` (`S -> S1`)
+//   * bounding placeholder (if any)
+//   * minimum universe
+// * And if we must be equal to a placeholder then we check it against
+//   * minimum universe
+//   * no bounding placeholder
+
+/// Tracks the "minimum universe" for each SCC, along with some region that
+/// caused it to change.
+#[derive(Copy, Clone, Debug)]
+struct SccUniverse<'tcx> {
+    /// For some SCC S, the minimum universe of:
+    ///
+    /// * each region R in S
+    /// * each SCC S1 such that S: S1
+    universe: ty::UniverseIndex,
+
+    /// Some region that caused `universe` to be what it is.
+    region: Option<ty::Region<'tcx>>,
+}
+
+impl<'tcx> SccUniverse<'tcx> {
+    /// If `universe` is less than our current universe, then update
+    /// `self.universe` and `self.region`.
+    fn take_min(&mut self, universe: ty::UniverseIndex, region: ty::Region<'tcx>) {
+        if universe < self.universe || self.region.is_none() {
+            self.universe = universe;
+            self.region = Some(region);
+        }
+    }
+}
+
+rustc_index::newtype_index! {
+    struct LeakCheckNode {
+        DEBUG_FORMAT = "LeakCheckNode({})"
+    }
+}
+
+rustc_index::newtype_index! {
+    struct LeakCheckScc {
+        DEBUG_FORMAT = "LeakCheckScc({})"
+    }
+}
+
+/// Represents the graph of constraints. For each `R1: R2` constraint we create
+/// an edge `R1 -> R2` in the graph.
+struct MiniGraph<'tcx> {
+    /// Map from a region to the index of the node in the graph.
+    nodes: FxHashMap<ty::Region<'tcx>, LeakCheckNode>,
+
+    /// Map from node index to SCC, and stores the successors of each SCC. All
+    /// the regions in the same SCC are equal to one another, and if `S1 -> S2`,
+    /// then `S1: S2`.
+    sccs: Sccs<LeakCheckNode, LeakCheckScc>,
+}
+
+impl<'tcx> MiniGraph<'tcx> {
+    fn new<'a>(
+        tcx: TyCtxt<'tcx>,
+        undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
+        verifys: &[Verify<'tcx>],
+    ) -> Self
+    where
+        'tcx: 'a,
+    {
+        let mut nodes = FxHashMap::default();
+        let mut edges = Vec::new();
+
+        // Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter.
+        Self::iterate_undo_log(tcx, undo_log, verifys, |target, source| {
+            let source_node = Self::add_node(&mut nodes, source);
+            let target_node = Self::add_node(&mut nodes, target);
+            edges.push((source_node, target_node));
+        });
+        let graph = VecGraph::new(nodes.len(), edges);
+        let sccs = Sccs::new(&graph);
+        Self { nodes, sccs }
+    }
+
+    /// Invokes `each_edge(R1, R2)` for each edge where `R2: R1`
+    fn iterate_undo_log<'a>(
+        tcx: TyCtxt<'tcx>,
+        undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
+        verifys: &[Verify<'tcx>],
+        mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>),
+    ) where
+        'tcx: 'a,
+    {
+        for undo_entry in undo_log {
+            match undo_entry {
+                &AddConstraint(Constraint::VarSubVar(a, b)) => {
+                    each_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b)));
+                }
+                &AddConstraint(Constraint::RegSubVar(a, b)) => {
+                    each_edge(a, tcx.mk_region(ReVar(b)));
+                }
+                &AddConstraint(Constraint::VarSubReg(a, b)) => {
+                    each_edge(tcx.mk_region(ReVar(a)), b);
+                }
+                &AddConstraint(Constraint::RegSubReg(a, b)) => {
+                    each_edge(a, b);
+                }
+                &AddGiven(a, b) => {
+                    each_edge(a, tcx.mk_region(ReVar(b)));
+                }
+                &AddVerify(i) => span_bug!(
+                    verifys[i].origin.span(),
+                    "we never add verifications while doing higher-ranked things",
+                ),
+                &AddCombination(..) | &AddVar(..) => {}
+            }
+        }
+    }
+
+    fn add_node(
+        nodes: &mut FxHashMap<ty::Region<'tcx>, LeakCheckNode>,
+        r: ty::Region<'tcx>,
+    ) -> LeakCheckNode {
+        let l = nodes.len();
+        *nodes.entry(r).or_insert(LeakCheckNode::new(l))
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
new file mode 100644
index 00000000000..7f4c33c5792
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -0,0 +1,812 @@
+//! See `README.md`.
+
+use self::CombineMapType::*;
+use self::UndoLog::*;
+
+use super::{
+    InferCtxtUndoLogs, MiscVariable, RegionVariableOrigin, Rollback, Snapshot, SubregionOrigin,
+};
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::undo_log::UndoLogs;
+use rustc_data_structures::unify as ut;
+use rustc_hir::def_id::DefId;
+use rustc_index::vec::IndexVec;
+use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion};
+use rustc_middle::ty::ReStatic;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{ReLateBound, ReVar};
+use rustc_middle::ty::{Region, RegionVid};
+use rustc_span::Span;
+
+use std::collections::BTreeMap;
+use std::ops::Range;
+use std::{cmp, fmt, mem};
+
+mod leak_check;
+
+pub use rustc_middle::infer::MemberConstraint;
+
+#[derive(Default)]
+pub struct RegionConstraintStorage<'tcx> {
+    /// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
+    var_infos: IndexVec<RegionVid, RegionVariableInfo>,
+
+    data: RegionConstraintData<'tcx>,
+
+    /// For a given pair of regions (R1, R2), maps to a region R3 that
+    /// is designated as their LUB (edges R1 <= R3 and R2 <= R3
+    /// exist). This prevents us from making many such regions.
+    lubs: CombineMap<'tcx>,
+
+    /// For a given pair of regions (R1, R2), maps to a region R3 that
+    /// is designated as their GLB (edges R3 <= R1 and R3 <= R2
+    /// exist). This prevents us from making many such regions.
+    glbs: CombineMap<'tcx>,
+
+    /// When we add a R1 == R2 constriant, we currently add (a) edges
+    /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this
+    /// table. You can then call `opportunistic_resolve_var` early
+    /// which will map R1 and R2 to some common region (i.e., either
+    /// R1 or R2). This is important when fulfillment, dropck and other such
+    /// code is iterating to a fixed point, because otherwise we sometimes
+    /// would wind up with a fresh stream of region variables that have been
+    /// equated but appear distinct.
+    pub(super) unification_table: ut::UnificationTableStorage<RegionVidKey<'tcx>>,
+
+    /// a flag set to true when we perform any unifications; this is used
+    /// to micro-optimize `take_and_reset_data`
+    any_unifications: bool,
+}
+
+pub struct RegionConstraintCollector<'a, 'tcx> {
+    storage: &'a mut RegionConstraintStorage<'tcx>,
+    undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
+}
+
+impl std::ops::Deref for RegionConstraintCollector<'_, 'tcx> {
+    type Target = RegionConstraintStorage<'tcx>;
+    #[inline]
+    fn deref(&self) -> &RegionConstraintStorage<'tcx> {
+        self.storage
+    }
+}
+
+impl std::ops::DerefMut for RegionConstraintCollector<'_, 'tcx> {
+    #[inline]
+    fn deref_mut(&mut self) -> &mut RegionConstraintStorage<'tcx> {
+        self.storage
+    }
+}
+
+pub type VarInfos = IndexVec<RegionVid, RegionVariableInfo>;
+
+/// The full set of region constraints gathered up by the collector.
+/// Describes constraints between the region variables and other
+/// regions, as well as other conditions that must be verified, or
+/// assumptions that can be made.
+#[derive(Debug, Default, Clone)]
+pub struct RegionConstraintData<'tcx> {
+    /// Constraints of the form `A <= B`, where either `A` or `B` can
+    /// be a region variable (or neither, as it happens).
+    pub constraints: BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
+
+    /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that
+    /// `R0` must be equal to one of the regions `R1..Rn`. These occur
+    /// with `impl Trait` quite frequently.
+    pub member_constraints: Vec<MemberConstraint<'tcx>>,
+
+    /// A "verify" is something that we need to verify after inference
+    /// is done, but which does not directly affect inference in any
+    /// way.
+    ///
+    /// An example is a `A <= B` where neither `A` nor `B` are
+    /// inference variables.
+    pub verifys: Vec<Verify<'tcx>>,
+
+    /// A "given" is a relationship that is known to hold. In
+    /// particular, we often know from closure fn signatures that a
+    /// particular free region must be a subregion of a region
+    /// variable:
+    ///
+    ///    foo.iter().filter(<'a> |x: &'a &'b T| ...)
+    ///
+    /// In situations like this, `'b` is in fact a region variable
+    /// introduced by the call to `iter()`, and `'a` is a bound region
+    /// on the closure (as indicated by the `<'a>` prefix). If we are
+    /// naive, we wind up inferring that `'b` must be `'static`,
+    /// because we require that it be greater than `'a` and we do not
+    /// know what `'a` is precisely.
+    ///
+    /// This hashmap is used to avoid that naive scenario. Basically
+    /// we record the fact that `'a <= 'b` is implied by the fn
+    /// signature, and then ignore the constraint when solving
+    /// equations. This is a bit of a hack but seems to work.
+    pub givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>,
+}
+
+/// Represents a constraint that influences the inference process.
+#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
+pub enum Constraint<'tcx> {
+    /// A region variable is a subregion of another.
+    VarSubVar(RegionVid, RegionVid),
+
+    /// A concrete region is a subregion of region variable.
+    RegSubVar(Region<'tcx>, RegionVid),
+
+    /// A region variable is a subregion of a concrete region. This does not
+    /// directly affect inference, but instead is checked after
+    /// inference is complete.
+    VarSubReg(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.
+    RegSubReg(Region<'tcx>, Region<'tcx>),
+}
+
+impl Constraint<'_> {
+    pub fn involves_placeholders(&self) -> bool {
+        match self {
+            Constraint::VarSubVar(_, _) => false,
+            Constraint::VarSubReg(_, r) | Constraint::RegSubVar(r, _) => r.is_placeholder(),
+            Constraint::RegSubReg(r, s) => r.is_placeholder() || s.is_placeholder(),
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct Verify<'tcx> {
+    pub kind: GenericKind<'tcx>,
+    pub origin: SubregionOrigin<'tcx>,
+    pub region: Region<'tcx>,
+    pub bound: VerifyBound<'tcx>,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable)]
+pub enum GenericKind<'tcx> {
+    Param(ty::ParamTy),
+    Projection(ty::ProjectionTy<'tcx>),
+}
+
+/// Describes the things that some `GenericKind` value `G` is known to
+/// outlive. Each variant of `VerifyBound` can be thought of as a
+/// function:
+///
+///     fn(min: Region) -> bool { .. }
+///
+/// where `true` means that the region `min` meets that `G: min`.
+/// (False means nothing.)
+///
+/// So, for example, if we have the type `T` and we have in scope that
+/// `T: 'a` and `T: 'b`, then the verify bound might be:
+///
+///     fn(min: Region) -> bool {
+///        ('a: min) || ('b: min)
+///     }
+///
+/// This is described with a `AnyRegion('a, 'b)` node.
+#[derive(Debug, Clone)]
+pub enum VerifyBound<'tcx> {
+    /// Given a kind K and a bound B, expands to a function like the
+    /// following, where `G` is the generic for which this verify
+    /// bound was created:
+    ///
+    /// ```rust
+    /// fn(min) -> bool {
+    ///     if G == K {
+    ///         B(min)
+    ///     } else {
+    ///         false
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// In other words, if the generic `G` that we are checking is
+    /// equal to `K`, then check the associated verify bound
+    /// (otherwise, false).
+    ///
+    /// This is used when we have something in the environment that
+    /// may or may not be relevant, depending on the region inference
+    /// results. For example, we may have `where <T as
+    /// Trait<'a>>::Item: 'b` in our where-clauses. If we are
+    /// generating the verify-bound for `<T as Trait<'0>>::Item`, then
+    /// this where-clause is only relevant if `'0` winds up inferred
+    /// to `'a`.
+    ///
+    /// So we would compile to a verify-bound like
+    ///
+    /// ```
+    /// IfEq(<T as Trait<'a>>::Item, AnyRegion('a))
+    /// ```
+    ///
+    /// meaning, if the subject G is equal to `<T as Trait<'a>>::Item`
+    /// (after inference), and `'a: min`, then `G: min`.
+    IfEq(Ty<'tcx>, Box<VerifyBound<'tcx>>),
+
+    /// Given a region `R`, expands to the function:
+    ///
+    /// ```
+    /// fn(min) -> bool {
+    ///     R: min
+    /// }
+    /// ```
+    ///
+    /// This is used when we can establish that `G: R` -- therefore,
+    /// if `R: min`, then by transitivity `G: min`.
+    OutlivedBy(Region<'tcx>),
+
+    /// Given a region `R`, true if it is `'empty`.
+    IsEmpty,
+
+    /// Given a set of bounds `B`, expands to the function:
+    ///
+    /// ```rust
+    /// fn(min) -> bool {
+    ///     exists (b in B) { b(min) }
+    /// }
+    /// ```
+    ///
+    /// In other words, if we meet some bound in `B`, that suffices.
+    /// This is used when all the bounds in `B` are known to apply to `G`.
+    AnyBound(Vec<VerifyBound<'tcx>>),
+
+    /// Given a set of bounds `B`, expands to the function:
+    ///
+    /// ```rust
+    /// fn(min) -> bool {
+    ///     forall (b in B) { b(min) }
+    /// }
+    /// ```
+    ///
+    /// In other words, if we meet *all* bounds in `B`, that suffices.
+    /// This is used when *some* bound in `B` is known to suffice, but
+    /// we don't know which.
+    AllBounds(Vec<VerifyBound<'tcx>>),
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub(crate) struct TwoRegions<'tcx> {
+    a: Region<'tcx>,
+    b: Region<'tcx>,
+}
+
+#[derive(Copy, Clone, PartialEq)]
+pub(crate) enum UndoLog<'tcx> {
+    /// We added `RegionVid`.
+    AddVar(RegionVid),
+
+    /// We added the given `constraint`.
+    AddConstraint(Constraint<'tcx>),
+
+    /// We added the given `verify`.
+    AddVerify(usize),
+
+    /// We added the given `given`.
+    AddGiven(Region<'tcx>, ty::RegionVid),
+
+    /// We added a GLB/LUB "combination variable".
+    AddCombination(CombineMapType, TwoRegions<'tcx>),
+}
+
+#[derive(Copy, Clone, PartialEq)]
+pub(crate) enum CombineMapType {
+    Lub,
+    Glb,
+}
+
+type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
+
+#[derive(Debug, Clone, Copy)]
+pub struct RegionVariableInfo {
+    pub origin: RegionVariableOrigin,
+    pub universe: ty::UniverseIndex,
+}
+
+pub struct RegionSnapshot {
+    any_unifications: bool,
+}
+
+impl<'tcx> RegionConstraintStorage<'tcx> {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    #[inline]
+    pub(crate) fn with_log<'a>(
+        &'a mut self,
+        undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
+    ) -> RegionConstraintCollector<'a, 'tcx> {
+        RegionConstraintCollector { storage: self, undo_log }
+    }
+
+    fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) {
+        match undo_entry {
+            AddVar(vid) => {
+                self.var_infos.pop().unwrap();
+                assert_eq!(self.var_infos.len(), vid.index() as usize);
+            }
+            AddConstraint(ref constraint) => {
+                self.data.constraints.remove(constraint);
+            }
+            AddVerify(index) => {
+                self.data.verifys.pop();
+                assert_eq!(self.data.verifys.len(), index);
+            }
+            AddGiven(sub, sup) => {
+                self.data.givens.remove(&(sub, sup));
+            }
+            AddCombination(Glb, ref regions) => {
+                self.glbs.remove(regions);
+            }
+            AddCombination(Lub, ref regions) => {
+                self.lubs.remove(regions);
+            }
+        }
+    }
+}
+
+impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
+    pub fn num_region_vars(&self) -> usize {
+        self.var_infos.len()
+    }
+
+    pub fn region_constraint_data(&self) -> &RegionConstraintData<'tcx> {
+        &self.data
+    }
+
+    /// Once all the constraints have been gathered, extract out the final data.
+    ///
+    /// Not legal during a snapshot.
+    pub fn into_infos_and_data(self) -> (VarInfos, RegionConstraintData<'tcx>) {
+        assert!(!UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
+        (mem::take(&mut self.storage.var_infos), mem::take(&mut self.storage.data))
+    }
+
+    /// Takes (and clears) the current set of constraints. Note that
+    /// the set of variables remains intact, but all relationships
+    /// between them are reset. This is used during NLL checking to
+    /// grab the set of constraints that arose from a particular
+    /// operation.
+    ///
+    /// We don't want to leak relationships between variables between
+    /// points because just because (say) `r1 == r2` was true at some
+    /// point P in the graph doesn't imply that it will be true at
+    /// some other point Q, in NLL.
+    ///
+    /// Not legal during a snapshot.
+    pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> {
+        assert!(!UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
+
+        // If you add a new field to `RegionConstraintCollector`, you
+        // should think carefully about whether it needs to be cleared
+        // or updated in some way.
+        let RegionConstraintStorage {
+            var_infos: _,
+            data,
+            lubs,
+            glbs,
+            unification_table: _,
+            any_unifications,
+        } = self.storage;
+
+        // Clear the tables of (lubs, glbs), so that we will create
+        // fresh regions if we do a LUB operation. As it happens,
+        // LUB/GLB are not performed by the MIR type-checker, which is
+        // the one that uses this method, but it's good to be correct.
+        lubs.clear();
+        glbs.clear();
+
+        let data = mem::take(data);
+
+        // Clear all unifications and recreate the variables a "now
+        // un-unified" state. Note that when we unify `a` and `b`, we
+        // also insert `a <= b` and a `b <= a` edges, so the
+        // `RegionConstraintData` contains the relationship here.
+        if *any_unifications {
+            *any_unifications = false;
+            self.unification_table().reset_unifications(|_| UnifiedRegion(None));
+        }
+
+        data
+    }
+
+    pub fn data(&self) -> &RegionConstraintData<'tcx> {
+        &self.data
+    }
+
+    pub fn start_snapshot(&mut self) -> RegionSnapshot {
+        debug!("RegionConstraintCollector: start_snapshot");
+        RegionSnapshot { any_unifications: self.any_unifications }
+    }
+
+    pub fn rollback_to(&mut self, snapshot: RegionSnapshot) {
+        debug!("RegionConstraintCollector: rollback_to({:?})", snapshot);
+        self.any_unifications = snapshot.any_unifications;
+    }
+
+    pub fn new_region_var(
+        &mut self,
+        universe: ty::UniverseIndex,
+        origin: RegionVariableOrigin,
+    ) -> RegionVid {
+        let vid = self.var_infos.push(RegionVariableInfo { origin, universe });
+
+        let u_vid = self.unification_table().new_key(UnifiedRegion(None));
+        assert_eq!(vid, u_vid.vid);
+        self.undo_log.push(AddVar(vid));
+        debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin);
+        vid
+    }
+
+    /// Returns the universe for the given variable.
+    pub fn var_universe(&self, vid: RegionVid) -> ty::UniverseIndex {
+        self.var_infos[vid].universe
+    }
+
+    fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) {
+        // cannot add constraints once regions are resolved
+        debug!("RegionConstraintCollector: add_constraint({:?})", constraint);
+
+        // never overwrite an existing (constraint, origin) - only insert one if it isn't
+        // present in the map yet. This prevents origins from outside the snapshot being
+        // replaced with "less informative" origins e.g., during calls to `can_eq`
+        let undo_log = &mut self.undo_log;
+        self.storage.data.constraints.entry(constraint).or_insert_with(|| {
+            undo_log.push(AddConstraint(constraint));
+            origin
+        });
+    }
+
+    fn add_verify(&mut self, verify: Verify<'tcx>) {
+        // cannot add verifys once regions are resolved
+        debug!("RegionConstraintCollector: add_verify({:?})", verify);
+
+        // skip no-op cases known to be satisfied
+        if let VerifyBound::AllBounds(ref bs) = verify.bound {
+            if bs.is_empty() {
+                return;
+            }
+        }
+
+        let index = self.data.verifys.len();
+        self.data.verifys.push(verify);
+        self.undo_log.push(AddVerify(index));
+    }
+
+    pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) {
+        // cannot add givens once regions are resolved
+        if self.data.givens.insert((sub, sup)) {
+            debug!("add_given({:?} <= {:?})", sub, sup);
+
+            self.undo_log.push(AddGiven(sub, sup));
+        }
+    }
+
+    pub fn make_eqregion(
+        &mut self,
+        origin: SubregionOrigin<'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);
+
+            match (sub, sup) {
+                (&ty::ReVar(sub), &ty::ReVar(sup)) => {
+                    debug!("make_eqregion: unifying {:?} with {:?}", sub, sup);
+                    self.unification_table().union(sub, sup);
+                    self.any_unifications = true;
+                }
+                (&ty::ReVar(vid), value) | (value, &ty::ReVar(vid)) => {
+                    debug!("make_eqregion: unifying {:?} with {:?}", vid, value);
+                    self.unification_table().union_value(vid, UnifiedRegion(Some(value)));
+                    self.any_unifications = true;
+                }
+                (_, _) => {}
+            }
+        }
+    }
+
+    pub fn member_constraint(
+        &mut self,
+        opaque_type_def_id: DefId,
+        definition_span: Span,
+        hidden_ty: Ty<'tcx>,
+        member_region: ty::Region<'tcx>,
+        choice_regions: &Lrc<Vec<ty::Region<'tcx>>>,
+    ) {
+        debug!("member_constraint({:?} in {:#?})", member_region, choice_regions);
+
+        if choice_regions.iter().any(|&r| r == member_region) {
+            return;
+        }
+
+        self.data.member_constraints.push(MemberConstraint {
+            opaque_type_def_id,
+            definition_span,
+            hidden_ty,
+            member_region,
+            choice_regions: choice_regions.clone(),
+        });
+    }
+
+    pub fn make_subregion(
+        &mut self,
+        origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) {
+        // cannot add constraints once regions are resolved
+        debug!(
+            "RegionConstraintCollector: make_subregion({:?}, {:?}) due to {:?}",
+            sub, sup, origin
+        );
+
+        match (sub, sup) {
+            (&ReLateBound(..), _) | (_, &ReLateBound(..)) => {
+                span_bug!(origin.span(), "cannot relate bound region: {:?} <= {:?}", sub, sup);
+            }
+            (_, &ReStatic) => {
+                // all regions are subregions of static, so we can ignore this
+            }
+            (&ReVar(sub_id), &ReVar(sup_id)) => {
+                self.add_constraint(Constraint::VarSubVar(sub_id, sup_id), origin);
+            }
+            (_, &ReVar(sup_id)) => {
+                self.add_constraint(Constraint::RegSubVar(sub, sup_id), origin);
+            }
+            (&ReVar(sub_id), _) => {
+                self.add_constraint(Constraint::VarSubReg(sub_id, sup), origin);
+            }
+            _ => {
+                self.add_constraint(Constraint::RegSubReg(sub, sup), origin);
+            }
+        }
+    }
+
+    pub fn verify_generic_bound(
+        &mut self,
+        origin: SubregionOrigin<'tcx>,
+        kind: GenericKind<'tcx>,
+        sub: Region<'tcx>,
+        bound: VerifyBound<'tcx>,
+    ) {
+        self.add_verify(Verify { kind, origin, region: sub, bound });
+    }
+
+    pub fn lub_regions(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        origin: SubregionOrigin<'tcx>,
+        a: Region<'tcx>,
+        b: Region<'tcx>,
+    ) -> Region<'tcx> {
+        // cannot add constraints once regions are resolved
+        debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b);
+        match (a, b) {
+            (r @ &ReStatic, _) | (_, r @ &ReStatic) => {
+                r // nothing lives longer than static
+            }
+
+            _ if a == b => {
+                a // LUB(a,a) = a
+            }
+
+            _ => self.combine_vars(tcx, Lub, a, b, origin),
+        }
+    }
+
+    pub fn glb_regions(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        origin: SubregionOrigin<'tcx>,
+        a: Region<'tcx>,
+        b: Region<'tcx>,
+    ) -> Region<'tcx> {
+        // cannot add constraints once regions are resolved
+        debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b);
+        match (a, b) {
+            (&ReStatic, r) | (r, &ReStatic) => {
+                r // static lives longer than everything else
+            }
+
+            _ if a == b => {
+                a // GLB(a,a) = a
+            }
+
+            _ => self.combine_vars(tcx, Glb, a, b, origin),
+        }
+    }
+
+    /// Resolves the passed RegionVid to the root RegionVid in the unification table
+    pub fn opportunistic_resolve_var(&mut self, rid: ty::RegionVid) -> ty::RegionVid {
+        self.unification_table().find(rid).vid
+    }
+
+    /// If the Region is a `ReVar`, then resolves it either to the root value in
+    /// the unification table, if it exists, or to the root `ReVar` in the table.
+    /// If the Region is not a `ReVar`, just returns the Region itself.
+    pub fn opportunistic_resolve_region(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        region: ty::Region<'tcx>,
+    ) -> ty::Region<'tcx> {
+        match region {
+            ty::ReVar(rid) => {
+                let unified_region = self.unification_table().probe_value(*rid);
+                unified_region.0.unwrap_or_else(|| {
+                    let root = self.unification_table().find(*rid).vid;
+                    tcx.reuse_or_mk_region(region, ty::ReVar(root))
+                })
+            }
+            _ => region,
+        }
+    }
+
+    fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> {
+        match t {
+            Glb => &mut self.glbs,
+            Lub => &mut self.lubs,
+        }
+    }
+
+    fn combine_vars(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        t: CombineMapType,
+        a: Region<'tcx>,
+        b: Region<'tcx>,
+        origin: SubregionOrigin<'tcx>,
+    ) -> Region<'tcx> {
+        let vars = TwoRegions { a, b };
+        if let Some(&c) = self.combine_map(t).get(&vars) {
+            return tcx.mk_region(ReVar(c));
+        }
+        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).insert(vars, c);
+        self.undo_log.push(AddCombination(t, vars));
+        let new_r = tcx.mk_region(ReVar(c));
+        for old_r in [a, b] {
+            match t {
+                Glb => self.make_subregion(origin.clone(), new_r, old_r),
+                Lub => self.make_subregion(origin.clone(), old_r, new_r),
+            }
+        }
+        debug!("combine_vars() c={:?}", c);
+        new_r
+    }
+
+    pub fn universe(&self, region: Region<'tcx>) -> ty::UniverseIndex {
+        match *region {
+            ty::ReStatic | ty::ReErased | ty::ReFree(..) | ty::ReEarlyBound(..) => {
+                ty::UniverseIndex::ROOT
+            }
+            ty::ReEmpty(ui) => ui,
+            ty::RePlaceholder(placeholder) => placeholder.universe,
+            ty::ReVar(vid) => self.var_universe(vid),
+            ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region),
+        }
+    }
+
+    pub fn vars_since_snapshot(
+        &self,
+        value_count: usize,
+    ) -> (Range<RegionVid>, Vec<RegionVariableOrigin>) {
+        let range = RegionVid::from(value_count)..RegionVid::from(self.unification_table.len());
+        (
+            range.clone(),
+            (range.start.index()..range.end.index())
+                .map(|index| self.var_infos[ty::RegionVid::from(index)].origin)
+                .collect(),
+        )
+    }
+
+    /// See `InferCtxt::region_constraints_added_in_snapshot`.
+    pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> Option<bool> {
+        self.undo_log
+            .region_constraints_in_snapshot(mark)
+            .map(|&elt| match elt {
+                AddConstraint(constraint) => Some(constraint.involves_placeholders()),
+                _ => None,
+            })
+            .max()
+            .unwrap_or(None)
+    }
+
+    #[inline]
+    fn unification_table(&mut self) -> super::UnificationTable<'_, 'tcx, RegionVidKey<'tcx>> {
+        ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log)
+    }
+}
+
+impl fmt::Debug for RegionSnapshot {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "RegionSnapshot")
+    }
+}
+
+impl<'tcx> fmt::Debug for GenericKind<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            GenericKind::Param(ref p) => write!(f, "{:?}", p),
+            GenericKind::Projection(ref p) => write!(f, "{:?}", p),
+        }
+    }
+}
+
+impl<'tcx> fmt::Display for GenericKind<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            GenericKind::Param(ref p) => write!(f, "{}", p),
+            GenericKind::Projection(ref p) => write!(f, "{}", p),
+        }
+    }
+}
+
+impl<'tcx> GenericKind<'tcx> {
+    pub fn to_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+        match *self {
+            GenericKind::Param(ref p) => p.to_ty(tcx),
+            GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs),
+        }
+    }
+}
+
+impl<'tcx> VerifyBound<'tcx> {
+    pub fn must_hold(&self) -> bool {
+        match self {
+            VerifyBound::IfEq(..) => false,
+            VerifyBound::OutlivedBy(ty::ReStatic) => true,
+            VerifyBound::OutlivedBy(_) => false,
+            VerifyBound::IsEmpty => false,
+            VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()),
+            VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()),
+        }
+    }
+
+    pub fn cannot_hold(&self) -> bool {
+        match self {
+            VerifyBound::IfEq(_, b) => b.cannot_hold(),
+            VerifyBound::IsEmpty => false,
+            VerifyBound::OutlivedBy(_) => false,
+            VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()),
+            VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()),
+        }
+    }
+
+    pub fn or(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> {
+        if self.must_hold() || vb.cannot_hold() {
+            self
+        } else if self.cannot_hold() || vb.must_hold() {
+            vb
+        } else {
+            VerifyBound::AnyBound(vec![self, vb])
+        }
+    }
+}
+
+impl<'tcx> RegionConstraintData<'tcx> {
+    /// Returns `true` if this region constraint data contains no constraints, and `false`
+    /// otherwise.
+    pub fn is_empty(&self) -> bool {
+        let RegionConstraintData { constraints, member_constraints, verifys, givens } = self;
+        constraints.is_empty()
+            && member_constraints.is_empty()
+            && verifys.is_empty()
+            && givens.is_empty()
+    }
+}
+
+impl<'tcx> Rollback<UndoLog<'tcx>> for RegionConstraintStorage<'tcx> {
+    fn reverse(&mut self, undo: UndoLog<'tcx>) {
+        self.rollback_undo_entry(undo)
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs
new file mode 100644
index 00000000000..48b8ee17594
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/resolve.rs
@@ -0,0 +1,250 @@
+use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use super::{FixupError, FixupResult, InferCtxt, Span};
+use rustc_middle::mir;
+use rustc_middle::ty::fold::{TypeFolder, TypeVisitor};
+use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable};
+
+use std::ops::ControlFlow;
+
+///////////////////////////////////////////////////////////////////////////
+// OPPORTUNISTIC VAR RESOLVER
+
+/// The opportunistic resolver can be used at any time. It simply replaces
+/// type/const variables that have been unified with the things they have
+/// been unified with (similar to `shallow_resolve`, but deep). This is
+/// useful for printing messages etc but also required at various
+/// points for correctness.
+pub struct OpportunisticVarResolver<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> {
+    #[inline]
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
+        OpportunisticVarResolver { infcx }
+    }
+}
+
+impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        if !t.has_infer_types_or_consts() {
+            t // micro-optimize -- if there is nothing in this type that this fold affects...
+        } else {
+            let t = self.infcx.shallow_resolve(t);
+            t.super_fold_with(self)
+        }
+    }
+
+    fn fold_const(&mut self, ct: &'tcx Const<'tcx>) -> &'tcx Const<'tcx> {
+        if !ct.has_infer_types_or_consts() {
+            ct // micro-optimize -- if there is nothing in this const that this fold affects...
+        } else {
+            let ct = self.infcx.shallow_resolve(ct);
+            ct.super_fold_with(self)
+        }
+    }
+
+    fn fold_mir_const(&mut self, constant: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
+        constant.super_fold_with(self)
+    }
+}
+
+/// The opportunistic region resolver opportunistically resolves regions
+/// variables to the variable with the least variable id. It is used when
+/// normlizing projections to avoid hitting the recursion limit by creating
+/// many versions of a predicate for types that in the end have to unify.
+///
+/// If you want to resolve type and const variables as well, call
+/// [InferCtxt::resolve_vars_if_possible] first.
+pub struct OpportunisticRegionResolver<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> OpportunisticRegionResolver<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
+        OpportunisticRegionResolver { infcx }
+    }
+}
+
+impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticRegionResolver<'a, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        if !t.has_infer_regions() {
+            t // micro-optimize -- if there is nothing in this type that this fold affects...
+        } else {
+            t.super_fold_with(self)
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReVar(rid) => {
+                let resolved = self
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .unwrap_region_constraints()
+                    .opportunistic_resolve_var(rid);
+                self.tcx().reuse_or_mk_region(r, ty::ReVar(resolved))
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        if !ct.has_infer_regions() {
+            ct // micro-optimize -- if there is nothing in this const that this fold affects...
+        } else {
+            ct.super_fold_with(self)
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// UNRESOLVED TYPE FINDER
+
+/// The unresolved type **finder** walks a type searching for
+/// type variables that don't yet have a value. The first unresolved type is stored.
+/// It does not construct the fully resolved type (which might
+/// involve some hashing and so forth).
+pub struct UnresolvedTypeFinder<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> UnresolvedTypeFinder<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
+        UnresolvedTypeFinder { infcx }
+    }
+}
+
+impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> {
+    type BreakTy = (Ty<'tcx>, Option<Span>);
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        let t = self.infcx.shallow_resolve(t);
+        if t.has_infer_types() {
+            if let ty::Infer(infer_ty) = *t.kind() {
+                // Since we called `shallow_resolve` above, this must
+                // be an (as yet...) unresolved inference variable.
+                let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty {
+                    let mut inner = self.infcx.inner.borrow_mut();
+                    let ty_vars = &inner.type_variables();
+                    if let TypeVariableOrigin {
+                        kind: TypeVariableOriginKind::TypeParameterDefinition(_, _),
+                        span,
+                    } = *ty_vars.var_origin(ty_vid)
+                    {
+                        Some(span)
+                    } else {
+                        None
+                    }
+                } else {
+                    None
+                };
+                ControlFlow::Break((t, ty_var_span))
+            } else {
+                // Otherwise, visit its contents.
+                t.super_visit_with(self)
+            }
+        } else {
+            // All type variables in inference types must already be resolved,
+            // - no need to visit the contents, continue visiting.
+            ControlFlow::CONTINUE
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// FULL TYPE RESOLUTION
+
+/// Full type resolution replaces all type and region variables with
+/// their concrete results. If any variable cannot be replaced (never unified, etc)
+/// then an `Err` result is returned.
+pub fn fully_resolve<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, value: T) -> FixupResult<'tcx, T>
+where
+    T: TypeFoldable<'tcx>,
+{
+    let mut full_resolver = FullTypeResolver { infcx, err: None };
+    let result = value.fold_with(&mut full_resolver);
+    match full_resolver.err {
+        None => Ok(result),
+        Some(e) => Err(e),
+    }
+}
+
+// N.B. This type is not public because the protocol around checking the
+// `err` field is not enforceable otherwise.
+struct FullTypeResolver<'a, 'tcx> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    err: Option<FixupError<'tcx>>,
+}
+
+impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        if !t.needs_infer() {
+            t // micro-optimize -- if there is nothing in this type that this fold affects...
+        } else {
+            let t = self.infcx.shallow_resolve(t);
+            match *t.kind() {
+                ty::Infer(ty::TyVar(vid)) => {
+                    self.err = Some(FixupError::UnresolvedTy(vid));
+                    self.tcx().ty_error()
+                }
+                ty::Infer(ty::IntVar(vid)) => {
+                    self.err = Some(FixupError::UnresolvedIntTy(vid));
+                    self.tcx().ty_error()
+                }
+                ty::Infer(ty::FloatVar(vid)) => {
+                    self.err = Some(FixupError::UnresolvedFloatTy(vid));
+                    self.tcx().ty_error()
+                }
+                ty::Infer(_) => {
+                    bug!("Unexpected type in full type resolver: {:?}", t);
+                }
+                _ => t.super_fold_with(self),
+            }
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReVar(rid) => self
+                .infcx
+                .lexical_region_resolutions
+                .borrow()
+                .as_ref()
+                .expect("region resolution not performed")
+                .resolve_var(rid),
+            _ => r,
+        }
+    }
+
+    fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        if !c.needs_infer() {
+            c // micro-optimize -- if there is nothing in this const that this fold affects...
+        } else {
+            let c = self.infcx.shallow_resolve(c);
+            match c.val {
+                ty::ConstKind::Infer(InferConst::Var(vid)) => {
+                    self.err = Some(FixupError::UnresolvedConst(vid));
+                    return self.tcx().const_error(c.ty);
+                }
+                ty::ConstKind::Infer(InferConst::Fresh(_)) => {
+                    bug!("Unexpected const in full const resolver: {:?}", c);
+                }
+                _ => {}
+            }
+            c.super_fold_with(self)
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs
new file mode 100644
index 00000000000..b3131936ae0
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/sub.rs
@@ -0,0 +1,180 @@
+use super::combine::{CombineFields, RelationDir};
+use super::SubregionOrigin;
+
+use crate::infer::combine::ConstEquateRelation;
+use crate::traits::Obligation;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::TyVar;
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
+use std::mem;
+
+/// Ensures `a` is made a subtype of `b`. Returns `a` on success.
+pub struct Sub<'combine, 'infcx, 'tcx> {
+    fields: &'combine mut CombineFields<'infcx, 'tcx>,
+    a_is_expected: bool,
+}
+
+impl<'combine, 'infcx, 'tcx> Sub<'combine, 'infcx, 'tcx> {
+    pub fn new(
+        f: &'combine mut CombineFields<'infcx, 'tcx>,
+        a_is_expected: bool,
+    ) -> Sub<'combine, 'infcx, 'tcx> {
+        Sub { fields: f, a_is_expected }
+    }
+
+    fn with_expected_switched<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
+        self.a_is_expected = !self.a_is_expected;
+        let result = f(self);
+        self.a_is_expected = !self.a_is_expected;
+        result
+    }
+}
+
+impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
+    fn tag(&self) -> &'static str {
+        "Sub"
+    }
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.fields.infcx.tcx
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.fields.param_env
+    }
+
+    fn a_is_expected(&self) -> bool {
+        self.a_is_expected
+    }
+
+    fn with_cause<F, R>(&mut self, cause: Cause, f: F) -> R
+    where
+        F: FnOnce(&mut Self) -> R,
+    {
+        debug!("sub with_cause={:?}", cause);
+        let old_cause = mem::replace(&mut self.fields.cause, Some(cause));
+        let r = f(self);
+        debug!("sub old_cause={:?}", old_cause);
+        self.fields.cause = old_cause;
+        r
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        match variance {
+            ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
+            ty::Covariant => self.relate(a, b),
+            ty::Bivariant => Ok(a),
+            ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)),
+        }
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        debug!("{}.tys({:?}, {:?})", self.tag(), a, b);
+
+        if a == b {
+            return Ok(a);
+        }
+
+        let infcx = self.fields.infcx;
+        let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
+        let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
+        match (a.kind(), b.kind()) {
+            (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => {
+                // Shouldn't have any LBR here, so we can safely put
+                // this under a binder below without fear of accidental
+                // capture.
+                assert!(!a.has_escaping_bound_vars());
+                assert!(!b.has_escaping_bound_vars());
+
+                // can't make progress on `A <: B` if both A and B are
+                // type variables, so record an obligation. We also
+                // have to record in the `type_variables` tracker that
+                // the two variables are equal modulo subtyping, which
+                // is important to the occurs check later on.
+                infcx.inner.borrow_mut().type_variables().sub(a_vid, b_vid);
+                self.fields.obligations.push(Obligation::new(
+                    self.fields.trace.cause.clone(),
+                    self.fields.param_env,
+                    ty::PredicateKind::Subtype(ty::SubtypePredicate {
+                        a_is_expected: self.a_is_expected,
+                        a,
+                        b,
+                    })
+                    .to_predicate(self.tcx()),
+                ));
+
+                Ok(a)
+            }
+            (&ty::Infer(TyVar(a_id)), _) => {
+                self.fields.instantiate(b, RelationDir::SupertypeOf, a_id, !self.a_is_expected)?;
+                Ok(a)
+            }
+            (_, &ty::Infer(TyVar(b_id))) => {
+                self.fields.instantiate(a, RelationDir::SubtypeOf, b_id, self.a_is_expected)?;
+                Ok(a)
+            }
+
+            (&ty::Error(_), _) | (_, &ty::Error(_)) => {
+                infcx.set_tainted_by_errors();
+                Ok(self.tcx().ty_error())
+            }
+
+            _ => {
+                self.fields.infcx.super_combine_tys(self, a, b)?;
+                Ok(a)
+            }
+        }
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug!("{}.regions({:?}, {:?}) self.cause={:?}", self.tag(), a, b, self.fields.cause);
+
+        // FIXME -- we have more fine-grained information available
+        // from the "cause" field, we could perhaps give more tailored
+        // error messages.
+        let origin = SubregionOrigin::Subtype(box self.fields.trace.clone());
+        self.fields
+            .infcx
+            .inner
+            .borrow_mut()
+            .unwrap_region_constraints()
+            .make_subregion(origin, a, b);
+
+        Ok(a)
+    }
+
+    fn consts(
+        &mut self,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        self.fields.infcx.super_combine_consts(self, a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        self.fields.higher_ranked_sub(a, b, self.a_is_expected)
+    }
+}
+
+impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> {
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs
new file mode 100644
index 00000000000..13b78b26af4
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/type_variable.rs
@@ -0,0 +1,446 @@
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::{self, Ty, TyVid};
+use rustc_span::symbol::Symbol;
+use rustc_span::Span;
+
+use crate::infer::InferCtxtUndoLogs;
+
+use rustc_data_structures::snapshot_vec as sv;
+use rustc_data_structures::unify as ut;
+use std::cmp;
+use std::marker::PhantomData;
+use std::ops::Range;
+
+use rustc_data_structures::undo_log::{Rollback, UndoLogs};
+
+/// Represents a single undo-able action that affects a type inference variable.
+pub(crate) enum UndoLog<'tcx> {
+    EqRelation(sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>),
+    SubRelation(sv::UndoLog<ut::Delegate<ty::TyVid>>),
+    Values(sv::UndoLog<Delegate>),
+}
+
+/// Convert from a specific kind of undo to the more general UndoLog
+impl<'tcx> From<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for UndoLog<'tcx> {
+    fn from(l: sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>) -> Self {
+        UndoLog::EqRelation(l)
+    }
+}
+
+/// Convert from a specific kind of undo to the more general UndoLog
+impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::TyVid>>> for UndoLog<'tcx> {
+    fn from(l: sv::UndoLog<ut::Delegate<ty::TyVid>>) -> Self {
+        UndoLog::SubRelation(l)
+    }
+}
+
+/// Convert from a specific kind of undo to the more general UndoLog
+impl<'tcx> From<sv::UndoLog<Delegate>> for UndoLog<'tcx> {
+    fn from(l: sv::UndoLog<Delegate>) -> Self {
+        UndoLog::Values(l)
+    }
+}
+
+/// Convert from a specific kind of undo to the more general UndoLog
+impl<'tcx> From<Instantiate> for UndoLog<'tcx> {
+    fn from(l: Instantiate) -> Self {
+        UndoLog::Values(sv::UndoLog::Other(l))
+    }
+}
+
+impl<'tcx> Rollback<UndoLog<'tcx>> for TypeVariableStorage<'tcx> {
+    fn reverse(&mut self, undo: UndoLog<'tcx>) {
+        match undo {
+            UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo),
+            UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo),
+            UndoLog::Values(undo) => self.values.reverse(undo),
+        }
+    }
+}
+
+pub struct TypeVariableStorage<'tcx> {
+    values: sv::SnapshotVecStorage<Delegate>,
+
+    /// Two variables are unified in `eq_relations` when we have a
+    /// constraint `?X == ?Y`. This table also stores, for each key,
+    /// the known value.
+    eq_relations: ut::UnificationTableStorage<TyVidEqKey<'tcx>>,
+
+    /// Two variables are unified in `sub_relations` when we have a
+    /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second
+    /// table exists only to help with the occurs check. In particular,
+    /// we want to report constraints like these as an occurs check
+    /// violation:
+    ///
+    ///     ?1 <: ?3
+    ///     Box<?3> <: ?1
+    ///
+    /// This works because `?1` and `?3` are unified in the
+    /// `sub_relations` relation (not in `eq_relations`). Then when we
+    /// process the `Box<?3> <: ?1` constraint, we do an occurs check
+    /// on `Box<?3>` and find a potential cycle.
+    ///
+    /// 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::UnificationTableStorage<ty::TyVid>,
+}
+
+pub struct TypeVariableTable<'a, 'tcx> {
+    storage: &'a mut TypeVariableStorage<'tcx>,
+
+    undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct TypeVariableOrigin {
+    pub kind: TypeVariableOriginKind,
+    pub span: Span,
+}
+
+/// Reasons to create a type inference variable
+#[derive(Copy, Clone, Debug)]
+pub enum TypeVariableOriginKind {
+    MiscVariable,
+    NormalizeProjectionType,
+    TypeInference,
+    TypeParameterDefinition(Symbol, Option<DefId>),
+
+    /// One of the upvars or closure kind parameters in a `ClosureSubsts`
+    /// (before it has been determined).
+    // FIXME(eddyb) distinguish upvar inference variables from the rest.
+    ClosureSynthetic,
+    SubstitutionPlaceholder,
+    AutoDeref,
+    AdjustmentType,
+    DivergingFn,
+    LatticeVariable,
+}
+
+pub(crate) struct TypeVariableData {
+    origin: TypeVariableOrigin,
+    diverging: bool,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum TypeVariableValue<'tcx> {
+    Known { value: Ty<'tcx> },
+    Unknown { universe: ty::UniverseIndex },
+}
+
+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,
+            TypeVariableValue::Known { value } => Some(value),
+        }
+    }
+
+    pub fn is_unknown(&self) -> bool {
+        match *self {
+            TypeVariableValue::Unknown { .. } => true,
+            TypeVariableValue::Known { .. } => false,
+        }
+    }
+}
+
+pub(crate) struct Instantiate;
+
+pub(crate) struct Delegate;
+
+impl<'tcx> TypeVariableStorage<'tcx> {
+    pub fn new() -> TypeVariableStorage<'tcx> {
+        TypeVariableStorage {
+            values: sv::SnapshotVecStorage::new(),
+            eq_relations: ut::UnificationTableStorage::new(),
+            sub_relations: ut::UnificationTableStorage::new(),
+        }
+    }
+
+    #[inline]
+    pub(crate) fn with_log<'a>(
+        &'a mut self,
+        undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
+    ) -> TypeVariableTable<'a, 'tcx> {
+        TypeVariableTable { storage: self, undo_log }
+    }
+}
+
+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(&self, vid: ty::TyVid) -> bool {
+        self.storage.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.storage.values.get(vid.index as usize).origin
+    }
+
+    /// Records that `a == b`, depending on `dir`.
+    ///
+    /// Precondition: neither `a` nor `b` are known.
+    pub fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) {
+        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);
+    }
+
+    /// Records that `a <: b`, depending on `dir`.
+    ///
+    /// Precondition: neither `a` nor `b` are known.
+    pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) {
+        debug_assert!(self.probe(a).is_unknown());
+        debug_assert!(self.probe(b).is_unknown());
+        self.sub_relations().union(a, b);
+    }
+
+    /// Instantiates `vid` with the type `ty`.
+    ///
+    /// 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_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 });
+
+        // Hack: we only need this so that `types_escaping_snapshot`
+        // can see what has been unified; see the Delegate impl for
+        // more details.
+        self.undo_log.push(Instantiate);
+    }
+
+    /// 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,
+        universe: ty::UniverseIndex,
+        diverging: bool,
+        origin: TypeVariableOrigin,
+    ) -> ty::TyVid {
+        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);
+
+        let index = self.values().push(TypeVariableData { origin, diverging });
+        assert_eq!(eq_key.vid.index, index as u32);
+
+        debug!(
+            "new_var(index={:?}, universe={:?}, diverging={:?}, origin={:?}",
+            eq_key.vid, universe, diverging, origin,
+        );
+
+        eq_key.vid
+    }
+
+    /// Returns the number of type variables created thus far.
+    pub fn num_vars(&self) -> usize {
+        self.storage.values.len()
+    }
+
+    /// Returns the "root" variable of `vid` in the `eq_relations`
+    /// equivalence table. All type variables that have been equated
+    /// will yield the same root variable (per the union-find
+    /// 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).vid
+    }
+
+    /// Returns the "root" variable of `vid` in the `sub_relations`
+    /// equivalence table. All type variables that have been are
+    /// related via equality or subtyping will yield the same root
+    /// variable (per the union-find algorithm), so `sub_root_var(a)
+    /// == sub_root_var(b)` implies that:
+    ///
+    ///     exists X. (a <: X || X <: a) && (b <: X || X <: b)
+    pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
+        self.sub_relations().find(vid)
+    }
+
+    /// Returns `true` if `a` and `b` have same "sub-root" (i.e., exists some
+    /// type X such that `forall i in {a, b}. (i <: X || X <: i)`.
+    pub fn sub_unified(&mut self, a: ty::TyVid, b: ty::TyVid) -> bool {
+        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) -> TypeVariableValue<'tcx> {
+        self.inlined_probe(vid)
+    }
+
+    /// An always-inlined variant of `probe`, for very hot call sites.
+    #[inline(always)]
+    pub fn inlined_probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> {
+        self.eq_relations().inlined_probe_value(vid)
+    }
+
+    /// 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.kind() {
+            ty::Infer(ty::TyVar(v)) => match self.probe(v) {
+                TypeVariableValue::Unknown { .. } => t,
+                TypeVariableValue::Known { value } => value,
+            },
+            _ => t,
+        }
+    }
+
+    #[inline]
+    fn values(
+        &mut self,
+    ) -> sv::SnapshotVec<Delegate, &mut Vec<TypeVariableData>, &mut InferCtxtUndoLogs<'tcx>> {
+        self.storage.values.with_log(self.undo_log)
+    }
+
+    #[inline]
+    fn eq_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidEqKey<'tcx>> {
+        self.storage.eq_relations.with_log(self.undo_log)
+    }
+
+    #[inline]
+    fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> {
+        self.storage.sub_relations.with_log(self.undo_log)
+    }
+
+    /// Returns a range of the type variables created during the snapshot.
+    pub fn vars_since_snapshot(
+        &mut self,
+        value_count: usize,
+    ) -> (Range<TyVid>, Vec<TypeVariableOrigin>) {
+        let range = TyVid { index: value_count as u32 }..TyVid { index: self.num_vars() as u32 };
+        (
+            range.start..range.end,
+            (range.start.index..range.end.index)
+                .map(|index| self.storage.values.get(index as usize).origin)
+                .collect(),
+        )
+    }
+
+    /// Returns indices of all variables that are not yet
+    /// instantiated.
+    pub fn unsolved_variables(&mut self) -> Vec<ty::TyVid> {
+        (0..self.storage.values.len())
+            .filter_map(|i| {
+                let vid = ty::TyVid { index: i as u32 };
+                match self.probe(vid) {
+                    TypeVariableValue::Unknown { .. } => Some(vid),
+                    TypeVariableValue::Known { .. } => None,
+                }
+            })
+            .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
+        // instantiation; 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)]
+pub(crate) 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>;
+    #[inline(always)]
+    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 { universe: universe1 },
+                &TypeVariableValue::Unknown { universe: universe2 },
+            ) => {
+                // If we unify two unbound variables, ?T and ?U, then whatever
+                // value they wind up taking (which must be the same value) must
+                // be nameable by both universes. Therefore, the resulting
+                // universe is the minimum of the two universes, because that is
+                // the one which contains the fewest names in scope.
+                let universe = cmp::min(universe1, universe2);
+                Ok(TypeVariableValue::Unknown { universe })
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs
new file mode 100644
index 00000000000..5ad2519a93c
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/undo_log.rs
@@ -0,0 +1,212 @@
+use std::marker::PhantomData;
+
+use rustc_data_structures::snapshot_vec as sv;
+use rustc_data_structures::undo_log::{Rollback, UndoLogs};
+use rustc_data_structures::unify as ut;
+use rustc_middle::infer::unify_key::RegionVidKey;
+use rustc_middle::ty;
+
+use crate::{
+    infer::{region_constraints, type_variable, InferCtxtInner},
+    traits,
+};
+
+pub struct Snapshot<'tcx> {
+    pub(crate) undo_len: usize,
+    _marker: PhantomData<&'tcx ()>,
+}
+
+/// Records the "undo" data for a single operation that affects some form of inference variable.
+pub(crate) enum UndoLog<'tcx> {
+    TypeVariables(type_variable::UndoLog<'tcx>),
+    ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
+    IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
+    FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
+    RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
+    RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
+    ProjectionCache(traits::UndoLog<'tcx>),
+    PushRegionObligation,
+}
+
+macro_rules! impl_from {
+    ($($ctor: ident ($ty: ty),)*) => {
+        $(
+        impl<'tcx> From<$ty> for UndoLog<'tcx> {
+            fn from(x: $ty) -> Self {
+                UndoLog::$ctor(x.into())
+            }
+        }
+        )*
+    }
+}
+
+// Upcast from a single kind of "undoable action" to the general enum
+impl_from! {
+    RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
+    TypeVariables(type_variable::UndoLog<'tcx>),
+
+    TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
+    TypeVariables(sv::UndoLog<ut::Delegate<ty::TyVid>>),
+    TypeVariables(sv::UndoLog<type_variable::Delegate>),
+    TypeVariables(type_variable::Instantiate),
+
+    IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
+
+    FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
+
+    ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
+
+    RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
+    ProjectionCache(traits::UndoLog<'tcx>),
+}
+
+/// The Rollback trait defines how to rollback a particular action.
+impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
+    fn reverse(&mut self, undo: UndoLog<'tcx>) {
+        match undo {
+            UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo),
+            UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
+            UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
+            UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
+            UndoLog::RegionConstraintCollector(undo) => {
+                self.region_constraint_storage.as_mut().unwrap().reverse(undo)
+            }
+            UndoLog::RegionUnificationTable(undo) => {
+                self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo)
+            }
+            UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo),
+            UndoLog::PushRegionObligation => {
+                self.region_obligations.pop();
+            }
+        }
+    }
+}
+
+/// The combined undo log for all the various unification tables. For each change to the storage
+/// for any kind of inference variable, we record an UndoLog entry in the vector here.
+pub(crate) struct InferCtxtUndoLogs<'tcx> {
+    logs: Vec<UndoLog<'tcx>>,
+    num_open_snapshots: usize,
+}
+
+impl Default for InferCtxtUndoLogs<'_> {
+    fn default() -> Self {
+        Self { logs: Default::default(), num_open_snapshots: Default::default() }
+    }
+}
+
+/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any
+/// action that is convertable into a UndoLog (per the From impls above).
+impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx>
+where
+    UndoLog<'tcx>: From<T>,
+{
+    #[inline]
+    fn num_open_snapshots(&self) -> usize {
+        self.num_open_snapshots
+    }
+
+    #[inline]
+    fn push(&mut self, undo: T) {
+        if self.in_snapshot() {
+            self.logs.push(undo.into())
+        }
+    }
+
+    fn clear(&mut self) {
+        self.logs.clear();
+        self.num_open_snapshots = 0;
+    }
+
+    fn extend<J>(&mut self, undos: J)
+    where
+        Self: Sized,
+        J: IntoIterator<Item = T>,
+    {
+        if self.in_snapshot() {
+            self.logs.extend(undos.into_iter().map(UndoLog::from))
+        }
+    }
+}
+
+impl<'tcx> InferCtxtInner<'tcx> {
+    pub fn rollback_to(&mut self, snapshot: Snapshot<'tcx>) {
+        debug!("rollback_to({})", snapshot.undo_len);
+        self.undo_log.assert_open_snapshot(&snapshot);
+
+        while self.undo_log.logs.len() > snapshot.undo_len {
+            let undo = self.undo_log.logs.pop().unwrap();
+            self.reverse(undo);
+        }
+
+        if self.undo_log.num_open_snapshots == 1 {
+            // The root snapshot. It's safe to clear the undo log because
+            // there's no snapshot further out that we might need to roll back
+            // to.
+            assert!(snapshot.undo_len == 0);
+            self.undo_log.logs.clear();
+        }
+
+        self.undo_log.num_open_snapshots -= 1;
+    }
+
+    pub fn commit(&mut self, snapshot: Snapshot<'tcx>) {
+        debug!("commit({})", snapshot.undo_len);
+
+        if self.undo_log.num_open_snapshots == 1 {
+            // The root snapshot. It's safe to clear the undo log because
+            // there's no snapshot further out that we might need to roll back
+            // to.
+            assert!(snapshot.undo_len == 0);
+            self.undo_log.logs.clear();
+        }
+
+        self.undo_log.num_open_snapshots -= 1;
+    }
+}
+
+impl<'tcx> InferCtxtUndoLogs<'tcx> {
+    pub fn start_snapshot(&mut self) -> Snapshot<'tcx> {
+        self.num_open_snapshots += 1;
+        Snapshot { undo_len: self.logs.len(), _marker: PhantomData }
+    }
+
+    pub(crate) fn region_constraints_in_snapshot(
+        &self,
+        s: &Snapshot<'tcx>,
+    ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
+        self.logs[s.undo_len..].iter().filter_map(|log| match log {
+            UndoLog::RegionConstraintCollector(log) => Some(log),
+            _ => None,
+        })
+    }
+
+    pub(crate) fn region_constraints(
+        &self,
+    ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
+        self.logs.iter().filter_map(|log| match log {
+            UndoLog::RegionConstraintCollector(log) => Some(log),
+            _ => None,
+        })
+    }
+
+    fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
+        // Failures here may indicate a failure to follow a stack discipline.
+        assert!(self.logs.len() >= snapshot.undo_len);
+        assert!(self.num_open_snapshots > 0);
+    }
+}
+
+impl<'tcx> std::ops::Index<usize> for InferCtxtUndoLogs<'tcx> {
+    type Output = UndoLog<'tcx>;
+
+    fn index(&self, key: usize) -> &Self::Output {
+        &self.logs[key]
+    }
+}
+
+impl<'tcx> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'tcx> {
+    fn index_mut(&mut self, key: usize) -> &mut Self::Output {
+        &mut self.logs[key]
+    }
+}
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
new file mode 100644
index 00000000000..ee358c52c2f
--- /dev/null
+++ b/compiler/rustc_infer/src/lib.rs
@@ -0,0 +1,38 @@
+//! This crates defines the type inference engine.
+//!
+//! - **Type inference.** The type inference code can be found in the `infer` module;
+//!   this code handles low-level equality and subtyping operations. The
+//!   type check pass in the compiler is found in the `rustc_typeck` crate.
+//!
+//! For more information about how rustc works, see the [rustc dev guide].
+//!
+//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(bool_to_option)]
+#![feature(box_patterns)]
+#![feature(box_syntax)]
+#![feature(extend_one)]
+#![feature(iter_zip)]
+#![feature(never_type)]
+#![feature(in_band_lifetimes)]
+#![feature(control_flow_enum)]
+#![feature(min_specialization)]
+#![recursion_limit = "512"] // For rustdoc
+
+#[macro_use]
+extern crate rustc_macros;
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[macro_use]
+extern crate rustc_data_structures;
+#[macro_use]
+extern crate tracing;
+#[macro_use]
+extern crate rustc_middle;
+
+pub mod infer;
+pub mod traits;
diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs
new file mode 100644
index 00000000000..2710debea94
--- /dev/null
+++ b/compiler/rustc_infer/src/traits/engine.rs
@@ -0,0 +1,78 @@
+use crate::infer::InferCtxt;
+use crate::traits::Obligation;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::{self, ToPredicate, Ty, WithConstness};
+
+use super::FulfillmentError;
+use super::{ObligationCause, PredicateObligation};
+
+pub trait TraitEngine<'tcx>: 'tcx {
+    fn normalize_projection_type(
+        &mut self,
+        infcx: &InferCtxt<'_, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        projection_ty: ty::ProjectionTy<'tcx>,
+        cause: ObligationCause<'tcx>,
+    ) -> Ty<'tcx>;
+
+    /// Requires that `ty` must implement the trait with `def_id` in
+    /// the given environment. This trait must not have any type
+    /// parameters (except for `Self`).
+    fn register_bound(
+        &mut self,
+        infcx: &InferCtxt<'_, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+        def_id: DefId,
+        cause: ObligationCause<'tcx>,
+    ) {
+        let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) };
+        self.register_predicate_obligation(
+            infcx,
+            Obligation {
+                cause,
+                recursion_depth: 0,
+                param_env,
+                predicate: trait_ref.without_const().to_predicate(infcx.tcx),
+            },
+        );
+    }
+
+    fn register_predicate_obligation(
+        &mut self,
+        infcx: &InferCtxt<'_, 'tcx>,
+        obligation: PredicateObligation<'tcx>,
+    );
+
+    fn select_all_or_error(
+        &mut self,
+        infcx: &InferCtxt<'_, 'tcx>,
+    ) -> Result<(), Vec<FulfillmentError<'tcx>>>;
+
+    fn select_where_possible(
+        &mut self,
+        infcx: &InferCtxt<'_, 'tcx>,
+    ) -> Result<(), Vec<FulfillmentError<'tcx>>>;
+
+    fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
+}
+
+pub trait TraitEngineExt<'tcx> {
+    fn register_predicate_obligations(
+        &mut self,
+        infcx: &InferCtxt<'_, 'tcx>,
+        obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
+    );
+}
+
+impl<T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T {
+    fn register_predicate_obligations(
+        &mut self,
+        infcx: &InferCtxt<'_, 'tcx>,
+        obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
+    ) {
+        for obligation in obligations {
+            self.register_predicate_obligation(infcx, obligation);
+        }
+    }
+}
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
new file mode 100644
index 00000000000..0ac4b6b25bb
--- /dev/null
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -0,0 +1,114 @@
+use super::ObjectSafetyViolation;
+
+use crate::infer::InferCtxt;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::Symbol;
+use rustc_span::{MultiSpan, Span};
+use std::fmt;
+use std::iter;
+
+impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    pub fn report_extra_impl_obligation(
+        &self,
+        error_span: Span,
+        item_name: Symbol,
+        _impl_item_def_id: DefId,
+        trait_item_def_id: DefId,
+        requirement: &dyn fmt::Display,
+    ) -> DiagnosticBuilder<'tcx> {
+        let msg = "impl has stricter requirements than trait";
+        let sp = self.tcx.sess.source_map().guess_head_span(error_span);
+
+        let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg);
+
+        if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) {
+            let span = self.tcx.sess.source_map().guess_head_span(trait_item_span);
+            err.span_label(span, format!("definition of `{}` from trait", item_name));
+        }
+
+        err.span_label(sp, format!("impl has extra requirement {}", requirement));
+
+        err
+    }
+}
+
+pub fn report_object_safety_error(
+    tcx: TyCtxt<'tcx>,
+    span: Span,
+    trait_def_id: DefId,
+    violations: &[ObjectSafetyViolation],
+) -> DiagnosticBuilder<'tcx> {
+    let trait_str = tcx.def_path_str(trait_def_id);
+    let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node {
+        hir::Node::Item(item) => Some(item.ident.span),
+        _ => None,
+    });
+    let span = tcx.sess.source_map().guess_head_span(span);
+    let mut err = struct_span_err!(
+        tcx.sess,
+        span,
+        E0038,
+        "the trait `{}` cannot be made into an object",
+        trait_str
+    );
+    err.span_label(span, format!("`{}` cannot be made into an object", trait_str));
+
+    let mut reported_violations = FxHashSet::default();
+    let mut multi_span = vec![];
+    let mut messages = vec![];
+    for violation in violations {
+        if let ObjectSafetyViolation::SizedSelf(sp) = &violation {
+            if !sp.is_empty() {
+                // Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations
+                // with a `Span`.
+                reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into()));
+            }
+        }
+        if reported_violations.insert(violation.clone()) {
+            let spans = violation.spans();
+            let msg = if trait_span.is_none() || spans.is_empty() {
+                format!("the trait cannot be made into an object because {}", violation.error_msg())
+            } else {
+                format!("...because {}", violation.error_msg())
+            };
+            if spans.is_empty() {
+                err.note(&msg);
+            } else {
+                for span in spans {
+                    multi_span.push(span);
+                    messages.push(msg.clone());
+                }
+            }
+            if trait_span.is_some() {
+                // Only provide the help if its a local trait, otherwise it's not actionable.
+                violation.solution(&mut err);
+            }
+        }
+    }
+    let has_multi_span = !multi_span.is_empty();
+    let mut note_span = MultiSpan::from_spans(multi_span.clone());
+    if let (Some(trait_span), true) = (trait_span, has_multi_span) {
+        note_span
+            .push_span_label(trait_span, "this trait cannot be made into an object...".to_string());
+    }
+    for (span, msg) in iter::zip(multi_span, messages) {
+        note_span.push_span_label(span, msg);
+    }
+    err.span_note(
+        note_span,
+        "for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \
+         to be resolvable dynamically; for more information visit \
+         <https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
+    );
+
+    if tcx.sess.trait_methods_not_found.borrow().iter().any(|full_span| full_span.contains(span)) {
+        // Avoid emitting error caused by non-existing method (#58734)
+        err.cancel();
+    }
+
+    err
+}
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs
new file mode 100644
index 00000000000..d5c17ede214
--- /dev/null
+++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -0,0 +1,139 @@
+//! Trait Resolution. See the [rustc-dev-guide] for more information on how this works.
+//!
+//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
+
+mod engine;
+pub mod error_reporting;
+mod project;
+mod structural_impls;
+pub mod util;
+
+use rustc_hir as hir;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::{self, Const, Ty};
+use rustc_span::Span;
+
+pub use self::FulfillmentErrorCode::*;
+pub use self::ImplSource::*;
+pub use self::ObligationCauseCode::*;
+pub use self::SelectionError::*;
+
+pub use self::engine::{TraitEngine, TraitEngineExt};
+pub use self::project::MismatchedProjectionTypes;
+pub(crate) use self::project::UndoLog;
+pub use self::project::{
+    Normalized, NormalizedTy, ProjectionCache, ProjectionCacheEntry, ProjectionCacheKey,
+    ProjectionCacheStorage, Reveal,
+};
+pub use rustc_middle::traits::*;
+
+/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for
+/// which the "impl_source" must be found. The process of finding a "impl_source" is
+/// called "resolving" the `Obligation`. This process consists of
+/// either identifying an `impl` (e.g., `impl Eq for i32`) that
+/// satisfies the obligation, or else finding a bound that is in
+/// scope. The eventual result is usually a `Selection` (defined below).
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct Obligation<'tcx, T> {
+    /// The reason we have to prove this thing.
+    pub cause: ObligationCause<'tcx>,
+
+    /// The environment in which we should prove this thing.
+    pub param_env: ty::ParamEnv<'tcx>,
+
+    /// The thing we are 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>>;
+pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
+
+// `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+static_assert_size!(PredicateObligation<'_>, 32);
+
+pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>;
+
+pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>;
+
+pub struct FulfillmentError<'tcx> {
+    pub obligation: PredicateObligation<'tcx>,
+    pub code: FulfillmentErrorCode<'tcx>,
+    /// Diagnostics only: we opportunistically change the `code.span` when we encounter an
+    /// obligation error caused by a call argument. When this is the case, we also signal that in
+    /// this field to ensure accuracy of suggestions.
+    pub points_at_arg_span: bool,
+    /// Diagnostics only: the 'root' obligation which resulted in
+    /// the failure to process `obligation`. This is the obligation
+    /// that was initially passed to `register_predicate_obligation`
+    pub root_obligation: PredicateObligation<'tcx>,
+}
+
+#[derive(Clone)]
+pub enum FulfillmentErrorCode<'tcx> {
+    CodeSelectionError(SelectionError<'tcx>),
+    CodeProjectionError(MismatchedProjectionTypes<'tcx>),
+    CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
+    CodeConstEquateError(ExpectedFound<&'tcx Const<'tcx>>, TypeError<'tcx>),
+    CodeAmbiguity,
+}
+
+impl<'tcx, O> Obligation<'tcx, O> {
+    pub fn new(
+        cause: ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        predicate: O,
+    ) -> Obligation<'tcx, O> {
+        Obligation { cause, param_env, recursion_depth: 0, predicate }
+    }
+
+    pub fn with_depth(
+        cause: ObligationCause<'tcx>,
+        recursion_depth: usize,
+        param_env: ty::ParamEnv<'tcx>,
+        predicate: O,
+    ) -> Obligation<'tcx, O> {
+        Obligation { cause, param_env, recursion_depth, predicate }
+    }
+
+    pub fn misc(
+        span: Span,
+        body_id: hir::HirId,
+        param_env: ty::ParamEnv<'tcx>,
+        trait_ref: O,
+    ) -> Obligation<'tcx, O> {
+        Obligation::new(ObligationCause::misc(span, body_id), param_env, trait_ref)
+    }
+
+    pub fn with<P>(&self, value: P) -> Obligation<'tcx, P> {
+        Obligation {
+            cause: self.cause.clone(),
+            param_env: self.param_env,
+            recursion_depth: self.recursion_depth,
+            predicate: value,
+        }
+    }
+}
+
+impl<'tcx> FulfillmentError<'tcx> {
+    pub fn new(
+        obligation: PredicateObligation<'tcx>,
+        code: FulfillmentErrorCode<'tcx>,
+        root_obligation: PredicateObligation<'tcx>,
+    ) -> FulfillmentError<'tcx> {
+        FulfillmentError { obligation, code, points_at_arg_span: false, root_obligation }
+    }
+}
+
+impl<'tcx> TraitObligation<'tcx> {
+    pub fn self_ty(&self) -> ty::Binder<'tcx, Ty<'tcx>> {
+        self.predicate.map_bound(|p| p.self_ty())
+    }
+}
diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs
new file mode 100644
index 00000000000..33bddf1dedc
--- /dev/null
+++ b/compiler/rustc_infer/src/traits/project.rs
@@ -0,0 +1,226 @@
+//! Code for projecting associated types out of trait references.
+
+use super::PredicateObligation;
+
+use crate::infer::InferCtxtUndoLogs;
+
+use rustc_data_structures::{
+    snapshot_map::{self, SnapshotMapRef, SnapshotMapStorage},
+    undo_log::Rollback,
+};
+use rustc_middle::ty::{self, Ty};
+
+pub use rustc_middle::traits::Reveal;
+
+pub(crate) type UndoLog<'tcx> =
+    snapshot_map::UndoLog<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>;
+
+#[derive(Clone)]
+pub struct MismatchedProjectionTypes<'tcx> {
+    pub err: ty::error::TypeError<'tcx>,
+}
+
+#[derive(Clone, TypeFoldable)]
+pub struct Normalized<'tcx, T> {
+    pub value: T,
+    pub obligations: Vec<PredicateObligation<'tcx>>,
+}
+
+pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>;
+
+impl<'tcx, T> Normalized<'tcx, T> {
+    pub fn with<U>(self, value: U) -> Normalized<'tcx, U> {
+        Normalized { value, obligations: self.obligations }
+    }
+}
+
+// # Cache
+
+/// The projection cache. Unlike the standard caches, this can include
+/// infcx-dependent type variables, therefore we have to roll the
+/// cache back each time we roll a snapshot back, to avoid assumptions
+/// on yet-unresolved inference variables. Types with placeholder
+/// regions also have to be removed when the respective snapshot ends.
+///
+/// Because of that, projection cache entries can be "stranded" and left
+/// inaccessible when type variables inside the key are resolved. We make no
+/// attempt to recover or remove "stranded" entries, but rather let them be
+/// (for the lifetime of the infcx).
+///
+/// Entries in the projection cache might contain inference variables
+/// that will be resolved by obligations on the projection cache entry (e.g.,
+/// when a type parameter in the associated type is constrained through
+/// an "RFC 447" projection on the impl).
+///
+/// When working with a fulfillment context, the derived obligations of each
+/// projection cache entry will be registered on the fulfillcx, so any users
+/// that can wait for a fulfillcx fixed point need not care about this. However,
+/// users that don't wait for a fixed point (e.g., trait evaluation) have to
+/// resolve the obligations themselves to make sure the projected result is
+/// ok and avoid issues like #43132.
+///
+/// If that is done, after evaluation the obligations, it is a good idea to
+/// call `ProjectionCache::complete` to make sure the obligations won't be
+/// re-evaluated and avoid an exponential worst-case.
+//
+// FIXME: we probably also want some sort of cross-infcx cache here to
+// reduce the amount of duplication. Let's see what we get with the Chalk reforms.
+pub struct ProjectionCache<'a, 'tcx> {
+    map: &'a mut SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
+    undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
+}
+
+#[derive(Default)]
+pub struct ProjectionCacheStorage<'tcx> {
+    map: SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
+}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct ProjectionCacheKey<'tcx> {
+    ty: ty::ProjectionTy<'tcx>,
+}
+
+impl ProjectionCacheKey<'tcx> {
+    pub fn new(ty: ty::ProjectionTy<'tcx>) -> Self {
+        Self { ty }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum ProjectionCacheEntry<'tcx> {
+    InProgress,
+    Ambiguous,
+    Recur,
+    Error,
+    NormalizedTy(NormalizedTy<'tcx>),
+}
+
+impl<'tcx> ProjectionCacheStorage<'tcx> {
+    #[inline]
+    pub(crate) fn with_log<'a>(
+        &'a mut self,
+        undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
+    ) -> ProjectionCache<'a, 'tcx> {
+        ProjectionCache { map: &mut self.map, undo_log }
+    }
+}
+
+impl<'tcx> ProjectionCache<'_, 'tcx> {
+    #[inline]
+    fn map(
+        &mut self,
+    ) -> SnapshotMapRef<
+        '_,
+        ProjectionCacheKey<'tcx>,
+        ProjectionCacheEntry<'tcx>,
+        InferCtxtUndoLogs<'tcx>,
+    > {
+        self.map.with_log(self.undo_log)
+    }
+
+    pub fn clear(&mut self) {
+        self.map().clear();
+    }
+
+    /// Try to start normalize `key`; returns an error if
+    /// normalization already occurred (this error corresponds to a
+    /// cache hit, so it's actually a good thing).
+    pub fn try_start(
+        &mut self,
+        key: ProjectionCacheKey<'tcx>,
+    ) -> Result<(), ProjectionCacheEntry<'tcx>> {
+        let mut map = self.map();
+        if let Some(entry) = map.get(&key) {
+            return Err(entry.clone());
+        }
+
+        map.insert(key, ProjectionCacheEntry::InProgress);
+        Ok(())
+    }
+
+    /// Indicates that `key` was normalized to `value`.
+    pub fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) {
+        debug!(
+            "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
+            key, value
+        );
+        let mut map = self.map();
+        if let Some(ProjectionCacheEntry::Recur) = map.get(&key) {
+            debug!("Not overwriting Recur");
+            return;
+        }
+        let fresh_key = map.insert(key, ProjectionCacheEntry::NormalizedTy(value));
+        assert!(!fresh_key, "never started projecting `{:?}`", key);
+    }
+
+    /// Mark the relevant projection cache key as having its derived obligations
+    /// complete, so they won't have to be re-computed (this is OK to do in a
+    /// snapshot - if the snapshot is rolled back, the obligations will be
+    /// marked as incomplete again).
+    pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) {
+        let mut map = self.map();
+        let ty = match map.get(&key) {
+            Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => {
+                debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty);
+                ty.value
+            }
+            ref value => {
+                // Type inference could "strand behind" old cache entries. Leave
+                // them alone for now.
+                debug!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", key, value);
+                return;
+            }
+        };
+
+        map.insert(
+            key,
+            ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }),
+        );
+    }
+
+    /// A specialized version of `complete` for when the key's value is known
+    /// to be a NormalizedTy.
+    pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) {
+        // We want to insert `ty` with no obligations. If the existing value
+        // already has no obligations (as is common) we don't insert anything.
+        if !ty.obligations.is_empty() {
+            self.map().insert(
+                key,
+                ProjectionCacheEntry::NormalizedTy(Normalized {
+                    value: ty.value,
+                    obligations: vec![],
+                }),
+            );
+        }
+    }
+
+    /// Indicates that trying to normalize `key` resulted in
+    /// ambiguity. No point in trying it again then until we gain more
+    /// type information (in which case, the "fully resolved" key will
+    /// be different).
+    pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) {
+        let fresh = self.map().insert(key, ProjectionCacheEntry::Ambiguous);
+        assert!(!fresh, "never started projecting `{:?}`", key);
+    }
+
+    /// Indicates that while trying to normalize `key`, `key` was required to
+    /// be normalized again. Selection or evaluation should eventually report
+    /// an error here.
+    pub fn recur(&mut self, key: ProjectionCacheKey<'tcx>) {
+        let fresh = self.map().insert(key, ProjectionCacheEntry::Recur);
+        assert!(!fresh, "never started projecting `{:?}`", key);
+    }
+
+    /// Indicates that trying to normalize `key` resulted in
+    /// error.
+    pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) {
+        let fresh = self.map().insert(key, ProjectionCacheEntry::Error);
+        assert!(!fresh, "never started projecting `{:?}`", key);
+    }
+}
+
+impl<'tcx> Rollback<UndoLog<'tcx>> for ProjectionCacheStorage<'tcx> {
+    fn reverse(&mut self, undo: UndoLog<'tcx>) {
+        self.map.reverse(undo);
+    }
+}
diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs
new file mode 100644
index 00000000000..c4a2ecee096
--- /dev/null
+++ b/compiler/rustc_infer/src/traits/structural_impls.rs
@@ -0,0 +1,75 @@
+use crate::traits;
+use crate::traits::project::Normalized;
+use rustc_middle::ty;
+use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
+
+use std::fmt;
+use std::ops::ControlFlow;
+
+// Structural impls for the structs in `traits`.
+
+impl<'tcx, T: fmt::Debug> fmt::Debug for Normalized<'tcx, T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Normalized({:?}, {:?})", self.value, self.obligations)
+    }
+}
+
+impl<'tcx, O: fmt::Debug> fmt::Debug for traits::Obligation<'tcx, O> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if ty::tls::with(|tcx| tcx.sess.verbose()) {
+            write!(
+                f,
+                "Obligation(predicate={:?}, cause={:?}, param_env={:?}, depth={})",
+                self.predicate, self.cause, self.param_env, self.recursion_depth
+            )
+        } else {
+            write!(f, "Obligation(predicate={:?}, depth={})", self.predicate, self.recursion_depth)
+        }
+    }
+}
+
+impl<'tcx> fmt::Debug for traits::FulfillmentError<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
+    }
+}
+
+impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            super::CodeSelectionError(ref e) => write!(f, "{:?}", e),
+            super::CodeProjectionError(ref e) => write!(f, "{:?}", e),
+            super::CodeSubtypeError(ref a, ref b) => {
+                write!(f, "CodeSubtypeError({:?}, {:?})", a, b)
+            }
+            super::CodeConstEquateError(ref a, ref b) => {
+                write!(f, "CodeConstEquateError({:?}, {:?})", a, b)
+            }
+            super::CodeAmbiguity => write!(f, "Ambiguity"),
+        }
+    }
+}
+
+impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "MismatchedProjectionTypes({:?})", self.err)
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// TypeFoldable implementations.
+
+impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx, O> {
+    fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
+        traits::Obligation {
+            cause: self.cause,
+            recursion_depth: self.recursion_depth,
+            predicate: self.predicate.fold_with(folder),
+            param_env: self.param_env.fold_with(folder),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+        self.predicate.visit_with(visitor)
+    }
+}
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
new file mode 100644
index 00000000000..ce1445f8a47
--- /dev/null
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -0,0 +1,354 @@
+use smallvec::smallvec;
+
+use crate::traits::{Obligation, ObligationCause, PredicateObligation};
+use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
+use rustc_middle::ty::outlives::Component;
+use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness};
+use rustc_span::symbol::Ident;
+
+pub fn anonymize_predicate<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    pred: ty::Predicate<'tcx>,
+) -> ty::Predicate<'tcx> {
+    let new = tcx.anonymize_late_bound_regions(pred.kind());
+    tcx.reuse_or_mk_predicate(pred, new)
+}
+
+pub struct PredicateSet<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    set: FxHashSet<ty::Predicate<'tcx>>,
+}
+
+impl PredicateSet<'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>) -> Self {
+        Self { tcx, set: Default::default() }
+    }
+
+    pub fn insert(&mut self, pred: ty::Predicate<'tcx>) -> bool {
+        // We have to be careful here because we want
+        //
+        //    for<'a> Foo<&'a i32>
+        //
+        // and
+        //
+        //    for<'b> Foo<&'b i32>
+        //
+        // to be considered equivalent. So normalize all late-bound
+        // regions before we throw things into the underlying set.
+        self.set.insert(anonymize_predicate(self.tcx, pred))
+    }
+}
+
+impl Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> {
+    fn extend<I: IntoIterator<Item = ty::Predicate<'tcx>>>(&mut self, iter: I) {
+        for pred in iter {
+            self.insert(pred);
+        }
+    }
+
+    fn extend_one(&mut self, pred: ty::Predicate<'tcx>) {
+        self.insert(pred);
+    }
+
+    fn extend_reserve(&mut self, additional: usize) {
+        Extend::<ty::Predicate<'tcx>>::extend_reserve(&mut self.set, additional);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// `Elaboration` iterator
+///////////////////////////////////////////////////////////////////////////
+
+/// "Elaboration" is the process of identifying all the predicates that
+/// are implied by a source predicate. Currently, this basically means
+/// walking the "supertraits" and other similar assumptions. For example,
+/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd`
+/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that
+/// `T: Foo`, then we know that `T: 'static`.
+pub struct Elaborator<'tcx> {
+    stack: Vec<PredicateObligation<'tcx>>,
+    visited: PredicateSet<'tcx>,
+}
+
+pub fn elaborate_trait_ref<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+) -> Elaborator<'tcx> {
+    elaborate_predicates(tcx, std::iter::once(trait_ref.without_const().to_predicate(tcx)))
+}
+
+pub fn elaborate_trait_refs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
+) -> Elaborator<'tcx> {
+    let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate(tcx));
+    elaborate_predicates(tcx, predicates)
+}
+
+pub fn elaborate_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
+) -> Elaborator<'tcx> {
+    let obligations = predicates
+        .map(|predicate| {
+            predicate_obligation(predicate, ty::ParamEnv::empty(), ObligationCause::dummy())
+        })
+        .collect();
+    elaborate_obligations(tcx, obligations)
+}
+
+pub fn elaborate_obligations<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mut obligations: Vec<PredicateObligation<'tcx>>,
+) -> Elaborator<'tcx> {
+    let mut visited = PredicateSet::new(tcx);
+    obligations.retain(|obligation| visited.insert(obligation.predicate));
+    Elaborator { stack: obligations, visited }
+}
+
+fn predicate_obligation<'tcx>(
+    predicate: ty::Predicate<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+) -> PredicateObligation<'tcx> {
+    Obligation { cause, param_env, recursion_depth: 0, predicate }
+}
+
+impl Elaborator<'tcx> {
+    pub fn filter_to_traits(self) -> FilterToTraits<Self> {
+        FilterToTraits::new(self)
+    }
+
+    fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) {
+        let tcx = self.visited.tcx;
+
+        let bound_predicate = obligation.predicate.kind();
+        match bound_predicate.skip_binder() {
+            ty::PredicateKind::Trait(data, _) => {
+                // Get predicates declared on the trait.
+                let predicates = tcx.super_predicates_of(data.def_id());
+
+                let obligations = predicates.predicates.iter().map(|&(pred, _)| {
+                    predicate_obligation(
+                        pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
+                        obligation.param_env,
+                        obligation.cause.clone(),
+                    )
+                });
+                debug!("super_predicates: data={:?}", data);
+
+                // 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 { }`.
+                let visited = &mut self.visited;
+                let obligations = obligations.filter(|o| visited.insert(o.predicate));
+
+                self.stack.extend(obligations);
+            }
+            ty::PredicateKind::WellFormed(..) => {
+                // Currently, we do not elaborate WF predicates,
+                // although we easily could.
+            }
+            ty::PredicateKind::ObjectSafe(..) => {
+                // Currently, we do not elaborate object-safe
+                // predicates.
+            }
+            ty::PredicateKind::Subtype(..) => {
+                // Currently, we do not "elaborate" predicates like `X <: Y`,
+                // though conceivably we might.
+            }
+            ty::PredicateKind::Projection(..) => {
+                // Nothing to elaborate in a projection predicate.
+            }
+            ty::PredicateKind::ClosureKind(..) => {
+                // Nothing to elaborate when waiting for a closure's kind to be inferred.
+            }
+            ty::PredicateKind::ConstEvaluatable(..) => {
+                // Currently, we do not elaborate const-evaluatable
+                // predicates.
+            }
+            ty::PredicateKind::ConstEquate(..) => {
+                // Currently, we do not elaborate const-equate
+                // predicates.
+            }
+            ty::PredicateKind::RegionOutlives(..) => {
+                // Nothing to elaborate from `'a: 'b`.
+            }
+            ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => {
+                // 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
+                if r_min.is_late_bound() {
+                    return;
+                }
+
+                let visited = &mut self.visited;
+                let mut components = smallvec![];
+                tcx.push_outlives_components(ty_max, &mut components);
+                self.stack.extend(
+                    components
+                        .into_iter()
+                        .filter_map(|component| match component {
+                            Component::Region(r) => {
+                                if r.is_late_bound() {
+                                    None
+                                } else {
+                                    Some(ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(
+                                        r, r_min,
+                                    )))
+                                }
+                            }
+
+                            Component::Param(p) => {
+                                let ty = tcx.mk_ty_param(p.index, p.name);
+                                Some(ty::PredicateKind::TypeOutlives(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
+                            }
+                        })
+                        .map(|predicate_kind| predicate_kind.to_predicate(tcx))
+                        .filter(|&predicate| visited.insert(predicate))
+                        .map(|predicate| {
+                            predicate_obligation(
+                                predicate,
+                                obligation.param_env,
+                                obligation.cause.clone(),
+                            )
+                        }),
+                );
+            }
+            ty::PredicateKind::TypeWellFormedFromEnv(..) => {
+                // Nothing to elaborate
+            }
+        }
+    }
+}
+
+impl Iterator for Elaborator<'tcx> {
+    type Item = PredicateObligation<'tcx>;
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.stack.len(), None)
+    }
+
+    fn next(&mut self) -> Option<Self::Item> {
+        // Extract next item from top-most stack frame, if any.
+        if let Some(obligation) = self.stack.pop() {
+            self.elaborate(&obligation);
+            Some(obligation)
+        } else {
+            None
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Supertrait iterator
+///////////////////////////////////////////////////////////////////////////
+
+pub type Supertraits<'tcx> = FilterToTraits<Elaborator<'tcx>>;
+
+pub fn supertraits<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+) -> Supertraits<'tcx> {
+    elaborate_trait_ref(tcx, trait_ref).filter_to_traits()
+}
+
+pub fn transitive_bounds<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
+) -> Supertraits<'tcx> {
+    elaborate_trait_refs(tcx, bounds).filter_to_traits()
+}
+
+/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may
+/// define the given associated type `assoc_name`. It uses the
+/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that
+/// aren't related to `assoc_item`.  This is used when resolving types like `Self::Item` or
+/// `T::Item` and helps to avoid cycle errors (see e.g. #35237).
+pub fn transitive_bounds_that_define_assoc_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
+    assoc_name: Ident,
+) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> {
+    let mut stack: Vec<_> = bounds.collect();
+    let mut visited = FxIndexSet::default();
+
+    std::iter::from_fn(move || {
+        while let Some(trait_ref) = stack.pop() {
+            let anon_trait_ref = tcx.anonymize_late_bound_regions(trait_ref);
+            if visited.insert(anon_trait_ref) {
+                let super_predicates = tcx.super_predicates_that_define_assoc_type((
+                    trait_ref.def_id(),
+                    Some(assoc_name),
+                ));
+                for (super_predicate, _) in super_predicates.predicates {
+                    let subst_predicate = super_predicate.subst_supertrait(tcx, &trait_ref);
+                    if let Some(binder) = subst_predicate.to_opt_poly_trait_ref() {
+                        stack.push(binder.value);
+                    }
+                }
+
+                return Some(trait_ref);
+            }
+        }
+
+        return None;
+    })
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Other
+///////////////////////////////////////////////////////////////////////////
+
+/// A filter around an iterator of predicates that makes it yield up
+/// just trait references.
+pub struct FilterToTraits<I> {
+    base_iterator: I,
+}
+
+impl<I> FilterToTraits<I> {
+    fn new(base: I) -> FilterToTraits<I> {
+        FilterToTraits { base_iterator: base }
+    }
+}
+
+impl<'tcx, I: Iterator<Item = PredicateObligation<'tcx>>> Iterator for FilterToTraits<I> {
+    type Item = ty::PolyTraitRef<'tcx>;
+
+    fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> {
+        while let Some(obligation) = self.base_iterator.next() {
+            if let Some(data) = obligation.predicate.to_opt_poly_trait_ref() {
+                return Some(data.value);
+            }
+        }
+        None
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (_, upper) = self.base_iterator.size_hint();
+        (0, upper)
+    }
+}