about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-02-07 23:08:52 +0000
committerbors <bors@rust-lang.org>2020-02-07 23:08:52 +0000
commit8498c5f5b02dbb4ed58a1eb4901b0b733342c35f (patch)
treef9c52c26c185f50112ce9a318e6c6fe173a3cc1a
parenta29424a2265411dda7d7446516ac5fd7499e2b55 (diff)
parent4b3c66d2c309a16d48c2b7f992a2038016a098d3 (diff)
downloadrust-8498c5f5b02dbb4ed58a1eb4901b0b733342c35f.tar.gz
rust-8498c5f5b02dbb4ed58a1eb4901b0b733342c35f.zip
Auto merge of #65232 - nikomatsakis:lazy-norm-anon-const-push-2, r=matthewjasper
replace the leak check with universes, take 2

This PR is an attempt to revive the "universe-based region check", which is an important step towards lazy normalization. Unlike before, we also modify the definition of `'empty` so that it is indexed by a universe. This sidesteps some of the surprising effects we saw before -- at the core, we no longer think that `exists<'a> { forall<'b> { 'b: 'a } }` is solveable. The new region lattice looks like this:

```
static ----------+-----...------+       (greatest)
|                |              |
early-bound and  |              |
free regions     |              |
|                |              |
scope regions    |              |
|                |              |
empty(root)   placeholder(U1)   |
|            /                  |
|           /         placeholder(Un)
empty(U1) --         /
|                   /
...                /
|                 /
empty(Un) --------                      (smallest)
```
This PR has three effects:

* It changes a fair number of error messages, I think for the better.
* It fixes a number of bugs. The old algorithm was too conservative and caused us to reject legal subtypings.
* It also causes two regressions (things that used to compile, but now do not).
    * `coherence-subtyping.rs` gets an additional error. This is expected.
    * `issue-57639.rs` regresses as before, for the reasons covered in #57639.

Both of the regressions stem from the same underlying property: without the leak check, the instantaneous "subtype" check is not able to tell whether higher-ranked subtyping will succeed or not. In both cases, we might be able to fix the problem by doing a 'leak-check like change' at some later point (e.g., as part of coherence).

This is a draft PR because:

* I didn't finish ripping out the leak-check completely.
* We might want to consider a crater run before landing this.
* We might want some kind of design meeting to cover the overall strategy.
* I just remembered I never finished 100% integrating this into the canonicalization code.
* I should also review what happens in NLL region checking -- it probably still has a notion of bottom (empty set).

r? @matthewjasper
-rw-r--r--src/librustc/ich/impls_ty.rs5
-rw-r--r--src/librustc/infer/canonical/canonicalizer.rs17
-rw-r--r--src/librustc/infer/combine.rs2
-rw-r--r--src/librustc/infer/error_reporting/mod.rs35
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs2
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/mod.rs18
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs2
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs19
-rw-r--r--src/librustc/infer/freshen.rs2
-rw-r--r--src/librustc/infer/higher_ranked/mod.rs10
-rw-r--r--src/librustc/infer/lexical_region_resolve/mod.rs148
-rw-r--r--src/librustc/infer/mod.rs36
-rw-r--r--src/librustc/infer/opaque_types/mod.rs8
-rw-r--r--src/librustc/infer/outlives/verify.rs13
-rw-r--r--src/librustc/infer/region_constraints/leak_check.rs12
-rw-r--r--src/librustc/infer/region_constraints/mod.rs8
-rw-r--r--src/librustc/middle/free_region.rs60
-rw-r--r--src/librustc/traits/coherence.rs15
-rw-r--r--src/librustc/traits/mod.rs22
-rw-r--r--src/librustc/traits/specialize/mod.rs6
-rw-r--r--src/librustc/traits/specialize/specialization_graph.rs24
-rw-r--r--src/librustc/ty/context.rs9
-rw-r--r--src/librustc/ty/free_region_map.rs89
-rw-r--r--src/librustc/ty/print/pretty.rs8
-rw-r--r--src/librustc/ty/structural_impls.rs2
-rw-r--r--src/librustc/ty/sty.rs85
-rw-r--r--src/librustc_index/vec.rs8
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/region_name.rs2
-rw-r--r--src/librustc_mir/borrow_check/region_infer/mod.rs5
-rw-r--r--src/librustc_mir/borrow_check/type_check/constraint_conversion.rs6
-rw-r--r--src/librustc_mir/borrow_check/type_check/free_region_relations.rs11
-rw-r--r--src/librustc_session/lint/builtin.rs11
-rw-r--r--src/librustc_traits/chalk_context/resolvent_ops.rs8
-rw-r--r--src/librustc_typeck/coherence/inherent_impls_overlap.rs5
-rw-r--r--src/librustc_typeck/collect.rs3
-rw-r--r--src/librustc_typeck/outlives/utils.rs2
-rw-r--r--src/librustc_typeck/variance/constraints.rs2
-rw-r--r--src/librustdoc/clean/mod.rs6
-rw-r--r--src/test/ui/coherence/coherence-inherited-subtyping.old.stderr14
-rw-r--r--src/test/ui/coherence/coherence-inherited-subtyping.re.stderr14
-rw-r--r--src/test/ui/coherence/coherence-inherited-subtyping.rs21
-rw-r--r--src/test/ui/coherence/coherence-subtyping.old.stderr14
-rw-r--r--src/test/ui/coherence/coherence-subtyping.re.stderr14
-rw-r--r--src/test/ui/coherence/coherence-subtyping.rs13
-rw-r--r--src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs4
45 files changed, 613 insertions, 207 deletions
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 6af0cee948d..844250f51a0 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -63,9 +63,12 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         mem::discriminant(self).hash_stable(hcx, hasher);
         match *self {
-            ty::ReErased | ty::ReStatic | ty::ReEmpty => {
+            ty::ReErased | ty::ReStatic => {
                 // No variant fields to hash for these ...
             }
+            ty::ReEmpty(universe) => {
+                universe.hash_stable(hcx, hasher);
+            }
             ty::ReLateBound(db, ty::BrAnon(i)) => {
                 db.hash_stable(hcx, hasher);
                 i.hash_stable(hcx, hasher);
diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs
index b720168f356..48a6c6d7413 100644
--- a/src/librustc/infer/canonical/canonicalizer.rs
+++ b/src/librustc/infer/canonical/canonicalizer.rs
@@ -167,11 +167,17 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
         r: ty::Region<'tcx>,
     ) -> ty::Region<'tcx> {
         match r {
-            ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => 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(
@@ -179,6 +185,11 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
                     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
@@ -213,7 +224,7 @@ impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
         r: ty::Region<'tcx>,
     ) -> ty::Region<'tcx> {
         match r {
-            ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReEmpty | ty::ReStatic => 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.
@@ -320,8 +331,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
             | ty::ReEarlyBound(..)
             | ty::ReFree(_)
             | ty::ReScope(_)
+            | ty::ReEmpty(_)
             | ty::RePlaceholder(..)
-            | ty::ReEmpty
             | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r),
 
             ty::ReClosureBound(..) => {
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 5d765a2a3d3..2518805a1ec 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -577,7 +577,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
 
             ty::RePlaceholder(..)
             | ty::ReVar(..)
-            | ty::ReEmpty
+            | ty::ReEmpty(_)
             | ty::ReStatic
             | ty::ReScope(..)
             | ty::ReEarlyBound(..)
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 58566bdcc35..57a52a991ed 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -138,7 +138,10 @@ pub(super) fn note_and_explain_region(
             msg_span_from_free_region(tcx, region)
         }
 
-        ty::ReEmpty => ("the empty lifetime".to_owned(), None),
+        ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), None),
+
+        // uh oh, hope no user ever sees THIS
+        ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), None),
 
         ty::RePlaceholder(_) => (format!("any other region"), None),
 
@@ -181,7 +184,8 @@ fn msg_span_from_free_region(
             msg_span_from_early_bound_and_free_regions(tcx, region)
         }
         ty::ReStatic => ("the static lifetime".to_owned(), None),
-        ty::ReEmpty => ("an empty lifetime".to_owned(), None),
+        ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), None),
+        ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), None),
         _ => bug!("{:?}", region),
     }
 }
@@ -375,6 +379,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         }
                     }
 
+                    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(
+                            region_scope_tree,
+                            sup_origin,
+                            sub_r,
+                            sup_r,
+                        )
+                        .emit();
+                    }
+
                     RegionResolutionError::MemberConstraintFailure {
                         opaque_type_def_id,
                         hidden_ty,
@@ -429,6 +458,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             RegionResolutionError::GenericBoundFailure(..) => true,
             RegionResolutionError::ConcreteFailure(..)
             | RegionResolutionError::SubSupConflict(..)
+            | RegionResolutionError::UpperBoundUniverseConflict(..)
             | RegionResolutionError::MemberConstraintFailure { .. } => false,
         };
 
@@ -443,6 +473,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             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
diff --git a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs
index 8f4c6439920..6a9fe19e1ac 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs
@@ -45,7 +45,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     ///
     /// 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();
+        let (span, sub, sup) = self.regions()?;
 
         // Determine whether the sub and sup consist of both anonymous (elided) regions.
         let anon_reg_sup = self.tcx().is_suitable_region(sup)?;
diff --git a/src/librustc/infer/error_reporting/nice_region_error/mod.rs b/src/librustc/infer/error_reporting/nice_region_error/mod.rs
index 8749d6cd34b..b10a60ef6f1 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/mod.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs
@@ -17,11 +17,6 @@ mod util;
 
 impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
     pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
-        match *error {
-            ConcreteFailure(..) | SubSupConflict(..) => {}
-            _ => return false, // inapplicable
-        }
-
         if let Some(tables) = self.in_progress_tables {
             let tables = tables.borrow();
             NiceRegionError::new(self, error.clone(), Some(&tables)).try_report().is_some()
@@ -79,13 +74,14 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
             .or_else(|| self.try_report_impl_not_conforming_to_trait())
     }
 
-    pub fn regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) {
+    pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
         match (&self.error, self.regions) {
-            (Some(ConcreteFailure(origin, sub, sup)), None) => (origin.span(), sub, sup),
-            (Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => (origin.span(), sub, sup),
-            (None, Some((span, sub, sup))) => (span, sub, sup),
-            (Some(_), Some(_)) => panic!("incorrectly built NiceRegionError"),
-            _ => panic!("trying to report on an incorrect lifetime failure"),
+            (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/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs
index df37f53606b..250dcff372c 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -9,7 +9,7 @@ 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<'a>> {
-        let (span, sub, sup) = self.regions();
+        let (span, sub, sup) = self.regions()?;
 
         debug!(
             "try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
diff --git a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs
index 7b31fe7cd7e..0b0bd61ce77 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -107,6 +107,25 @@ impl NiceRegionError<'me, 'tcx> {
                 found.substs,
             )),
 
+            Some(RegionResolutionError::UpperBoundUniverseConflict(
+                vid,
+                _,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace {
+                    cause,
+                    values: ValuePairs::TraitRefs(ExpectedFound { expected, found }),
+                }),
+                sup_placeholder @ ty::RePlaceholder(_),
+            )) if expected.def_id == found.def_id => Some(self.try_report_placeholders_trait(
+                Some(self.tcx().mk_region(ty::ReVar(*vid))),
+                cause,
+                None,
+                Some(*sup_placeholder),
+                expected.def_id,
+                expected.substs,
+                found.substs,
+            )),
+
             Some(RegionResolutionError::ConcreteFailure(
                 SubregionOrigin::Subtype(box TypeTrace {
                     cause,
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index 16087959972..cf61cac0ac4 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -130,7 +130,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
             | ty::ReScope(_)
             | ty::ReVar(_)
             | ty::RePlaceholder(..)
-            | ty::ReEmpty
+            | ty::ReEmpty(_)
             | ty::ReErased => {
                 // replace all free regions with 'erased
                 self.tcx().lifetimes.re_erased
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index bbca4823431..d25d186f4d7 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -128,6 +128,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         placeholder_map: &PlaceholderMap<'tcx>,
         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.borrow_region_constraints().leak_check(
             self.tcx,
             overly_polymorphic,
diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs
index 4b1f8a5be14..e0a8c3b4e65 100644
--- a/src/librustc/infer/lexical_region_resolve/mod.rs
+++ b/src/librustc/infer/lexical_region_resolve/mod.rs
@@ -82,6 +82,16 @@ pub enum RegionResolutionError<'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.
@@ -149,7 +159,14 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
     fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> {
         LexicalRegionResolutions {
             error_region: tcx.lifetimes.re_static,
-            values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars()),
+            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(),
+            ),
         }
     }
 
@@ -381,8 +398,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                 // This is a specialized version of the `lub_concrete_regions`
                 // check below for a common case, here purely as an
                 // optimization.
-                if let ReEmpty = a_region {
-                    return false;
+                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);
@@ -399,7 +419,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                 // tighter bound than `'static`.
                 //
                 // (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.)
-                let b_universe = self.var_infos[b_vid].universe;
                 if let ty::RePlaceholder(p) = lub {
                     if b_universe.cannot_name(p.universe) {
                         lub = self.tcx().lifetimes.re_static;
@@ -420,12 +439,38 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
 
     /// 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 smallest region `c` such that `a <= c` and `b <= c`.
+    /// 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> {
-        match (a, b) {
+        let r = match (a, b) {
             (&ty::ReClosureBound(..), _)
             | (_, &ty::ReClosureBound(..))
             | (&ReLateBound(..), _)
@@ -435,14 +480,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                 bug!("cannot relate region: LUB({:?}, {:?})", a, b);
             }
 
-            (r @ &ReStatic, _) | (_, r @ &ReStatic) => {
-                r // nothing lives longer than static
-            }
-
-            (&ReEmpty, r) | (r, &ReEmpty) => {
-                r // everything lives longer than empty
-            }
-
             (&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
                 span_bug!(
                     self.var_infos[v_id].origin.span(),
@@ -453,6 +490,41 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                 );
             }
 
+            (&ReStatic, _) | (_, &ReStatic) => {
+                // nothing lives longer than `'static`
+                self.tcx().lifetimes.re_static
+            }
+
+            (&ReEmpty(_), r @ ReEarlyBound(_))
+            | (r @ ReEarlyBound(_), &ReEmpty(_))
+            | (&ReEmpty(_), r @ ReFree(_))
+            | (r @ ReFree(_), &ReEmpty(_))
+            | (&ReEmpty(_), r @ ReScope(_))
+            | (r @ ReScope(_), &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(_), &ReScope(s_id))
             | (&ReScope(s_id), &ReEarlyBound(_))
             | (&ReFree(_), &ReScope(s_id))
@@ -509,7 +581,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                     self.tcx().lifetimes.re_static
                 }
             }
-        }
+        };
+
+        debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);
+
+        r
     }
 
     /// After expansion is complete, go and check upper bounds (i.e.,
@@ -528,7 +604,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                 }
 
                 Constraint::RegSubReg(sub, sup) => {
-                    if self.region_rels.is_subregion_of(sub, sup) {
+                    if self.sub_concrete_regions(sub, sup) {
                         continue;
                     }
 
@@ -557,7 +633,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
                     // Do not report these errors immediately:
                     // instead, set the variable value to error and
                     // collect them later.
-                    if !self.region_rels.is_subregion_of(a_region, b_region) {
+                    if !self.sub_concrete_regions(a_region, b_region) {
                         debug!(
                             "collect_errors: region error at {:?}: \
                              cannot verify that {:?}={:?} <= {:?}",
@@ -592,12 +668,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
             debug!("collect_errors: verify={:?}", verify);
             let sub = var_data.normalize(self.tcx(), verify.region);
 
-            // This was an inference variable which didn't get
-            // constrained, therefore it can be assume to hold.
-            if let ty::ReEmpty = *sub {
-                continue;
-            }
-
             let verify_kind_ty = verify.kind.to_ty(self.tcx());
             if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) {
                 continue;
@@ -760,7 +830,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
             };
 
             for upper_bound in &upper_bounds {
-                if !self.region_rels.is_subregion_of(effective_lower_bound, upper_bound.region) {
+                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: {:?} \
@@ -780,6 +850,26 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
             }
         }
 
+        // 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` doesn't permit those placeholders.
+        for upper_bound in &upper_bounds {
+            if let ty::RePlaceholder(p) = upper_bound.region {
+                if node_universe.cannot_name(p.universe) {
+                    let origin = self.var_infos[node_idx].origin.clone();
+                    errors.push(RegionResolutionError::UpperBoundUniverseConflict(
+                        node_idx,
+                        origin,
+                        node_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(
@@ -890,7 +980,15 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
             }
 
             VerifyBound::OutlivedBy(r) => {
-                self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r))
+                self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r))
+            }
+
+            VerifyBound::IsEmpty => {
+                if let ty::ReEmpty(_) = min {
+                    true
+                } else {
+                    false
+                }
             }
 
             VerifyBound::AnyBound(bs) => {
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 4681a47317c..b93f4408cdc 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -125,6 +125,13 @@ pub struct InferCtxt<'a, 'tcx> {
     /// order, represented by its upper and lower bounds.
     pub type_variables: RefCell<type_variable::TypeVariableTable<'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>,
+
     /// Map from const parameter variable to the kind of const it represents.
     const_unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::ConstVid<'tcx>>>>,
 
@@ -246,7 +253,7 @@ pub enum ValuePairs<'tcx> {
 /// encounter an error or subtyping constraint.
 ///
 /// See the `error_reporting` module for more details.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct TypeTrace<'tcx> {
     cause: ObligationCause<'tcx>,
     values: ValuePairs<'tcx>,
@@ -550,6 +557,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
                 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),
                 region_obligations: RefCell::new(vec![]),
                 universe: Cell::new(ty::UniverseIndex::ROOT),
             })
@@ -593,6 +601,7 @@ pub struct CombinedSnapshot<'a, 'tcx> {
     region_obligations_snapshot: usize,
     universe: ty::UniverseIndex,
     was_in_snapshot: bool,
+    was_skip_leak_check: bool,
     _in_progress_tables: Option<Ref<'a, ty::TypeckTables<'tcx>>>,
 }
 
@@ -720,6 +729,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             region_obligations_snapshot: self.region_obligations.borrow().len(),
             universe: self.universe(),
             was_in_snapshot: in_snapshot,
+            was_skip_leak_check: self.skip_leak_check.get(),
             // Borrow tables "in progress" (i.e., during typeck)
             // to ban writes from within a snapshot to them.
             _in_progress_tables: self.in_progress_tables.map(|tables| tables.borrow()),
@@ -738,11 +748,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             region_obligations_snapshot,
             universe,
             was_in_snapshot,
+            was_skip_leak_check,
             _in_progress_tables,
         } = snapshot;
 
         self.in_snapshot.set(was_in_snapshot);
         self.universe.set(universe);
+        self.skip_leak_check.set(was_skip_leak_check);
 
         self.projection_cache.borrow_mut().rollback_to(projection_cache_snapshot);
         self.type_variables.borrow_mut().rollback_to(type_snapshot);
@@ -765,10 +777,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             region_obligations_snapshot: _,
             universe: _,
             was_in_snapshot,
+            was_skip_leak_check,
             _in_progress_tables,
         } = snapshot;
 
         self.in_snapshot.set(was_in_snapshot);
+        self.skip_leak_check.set(was_skip_leak_check);
 
         self.projection_cache.borrow_mut().commit(projection_cache_snapshot);
         self.type_variables.borrow_mut().commit(type_snapshot);
@@ -822,6 +836,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         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 skip_leak_check = should_skip || self.skip_leak_check.get();
+        self.skip_leak_check.set(skip_leak_check);
+        let r = f(&snapshot);
+        self.rollback_to("probe", snapshot);
+        r
+    }
+
     /// Scan the constraints produced since `snapshot` began and returns:
     ///
     /// - `None` -- if none of them involve "region outlives" constraints
@@ -1647,12 +1675,6 @@ impl<'tcx> TypeTrace<'tcx> {
     }
 }
 
-impl<'tcx> fmt::Debug for TypeTrace<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "TypeTrace({:?})", self.cause)
-    }
-}
-
 impl<'tcx> SubregionOrigin<'tcx> {
     pub fn span(&self) -> Span {
         match *self {
diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs
index d28507f6eb2..7fef9d27c4d 100644
--- a/src/librustc/infer/opaque_types/mod.rs
+++ b/src/librustc/infer/opaque_types/mod.rs
@@ -384,9 +384,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             match least_region {
                 None => least_region = Some(subst_arg),
                 Some(lr) => {
-                    if free_region_relations.sub_free_regions(lr, subst_arg) {
+                    if free_region_relations.sub_free_regions(self.tcx, lr, subst_arg) {
                         // keep the current least region
-                    } else if free_region_relations.sub_free_regions(subst_arg, lr) {
+                    } else if free_region_relations.sub_free_regions(self.tcx, subst_arg, lr) {
                         // switch to `subst_arg`
                         least_region = Some(subst_arg);
                     } else {
@@ -611,7 +611,7 @@ pub fn unexpected_hidden_region_diagnostic(
     );
 
     // Explain the region we are capturing.
-    if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty = hidden_region {
+    if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) = hidden_region {
         // 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
@@ -844,7 +844,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
                         .emit();
                     }
                 }
-                self.tcx.lifetimes.re_empty
+                self.tcx.lifetimes.re_root_empty
             }
             None => {
                 self.tcx
diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs
index 8ee8482e79d..a2c99064caa 100644
--- a/src/librustc/infer/outlives/verify.rs
+++ b/src/librustc/infer/outlives/verify.rs
@@ -60,7 +60,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         // scope type parameters:
         let param_bounds = param_bounds.chain(self.implicit_region_bound);
 
-        VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect())
+        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
diff --git a/src/librustc/infer/region_constraints/leak_check.rs b/src/librustc/infer/region_constraints/leak_check.rs
index 3b3a464ba55..29290cef2d2 100644
--- a/src/librustc/infer/region_constraints/leak_check.rs
+++ b/src/librustc/infer/region_constraints/leak_check.rs
@@ -33,18 +33,6 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
 
         assert!(self.in_snapshot());
 
-        // If the user gave `-Zno-leak-check`, then skip the leak
-        // check completely. This is wildly unsound and also not
-        // unlikely to cause an ICE or two. It is intended for use
-        // only during a transition period, in which the MIR typeck
-        // uses the "universe-style" check, and the rest of typeck
-        // uses the more conservative leak check.  Since the leak
-        // check is more conservative, we can't test the
-        // universe-style check without disabling it.
-        if tcx.sess.opts.debugging_opts.no_leak_check {
-            return Ok(());
-        }
-
         // Go through each placeholder that we created.
         for (_, &placeholder_region) in placeholder_map {
             // Find the universe this placeholder inhabits.
diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs
index 410058b70b5..2c580e2e349 100644
--- a/src/librustc/infer/region_constraints/mod.rs
+++ b/src/librustc/infer/region_constraints/mod.rs
@@ -233,6 +233,9 @@ pub enum VerifyBound<'tcx> {
     /// 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
@@ -792,10 +795,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         match *region {
             ty::ReScope(..)
             | ty::ReStatic
-            | ty::ReEmpty
             | ty::ReErased
             | ty::ReFree(..)
             | ty::ReEarlyBound(..) => ty::UniverseIndex::ROOT,
+            ty::ReEmpty(ui) => ui,
             ty::RePlaceholder(placeholder) => placeholder.universe,
             ty::ReClosureBound(vid) | ty::ReVar(vid) => self.var_universe(vid),
             ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region),
@@ -867,6 +870,7 @@ impl<'tcx> VerifyBound<'tcx> {
             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()),
         }
@@ -875,7 +879,7 @@ impl<'tcx> VerifyBound<'tcx> {
     pub fn cannot_hold(&self) -> bool {
         match self {
             VerifyBound::IfEq(_, b) => b.cannot_hold(),
-            VerifyBound::OutlivedBy(ty::ReEmpty) => true,
+            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()),
diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs
index 355f949b870..62ccd946744 100644
--- a/src/librustc/middle/free_region.rs
+++ b/src/librustc/middle/free_region.rs
@@ -4,8 +4,8 @@
 //! and use that to decide when one free region outlives another, and so forth.
 
 use crate::middle::region;
-use crate::ty::free_region_map::{FreeRegionMap, FreeRegionRelations};
-use crate::ty::{self, Region, TyCtxt};
+use crate::ty::free_region_map::FreeRegionMap;
+use crate::ty::{Region, TyCtxt};
 use rustc_hir::def_id::DefId;
 
 /// Combines a `region::ScopeTree` (which governs relationships between
@@ -38,62 +38,6 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> {
         Self { tcx, context, region_scope_tree, free_regions }
     }
 
-    /// Determines whether one region is a subregion of another. This is intended to run *after
-    /// inference* and sadly the logic is somewhat duplicated with the code in infer.rs.
-    pub fn is_subregion_of(
-        &self,
-        sub_region: ty::Region<'tcx>,
-        super_region: ty::Region<'tcx>,
-    ) -> bool {
-        let result = sub_region == super_region || {
-            match (sub_region, super_region) {
-                (ty::ReEmpty, _) | (_, ty::ReStatic) => true,
-
-                (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => {
-                    self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope)
-                }
-
-                (ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => {
-                    let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br);
-                    self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
-                }
-
-                (ty::ReScope(sub_scope), ty::ReFree(fr)) => {
-                    let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
-                    self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
-                }
-
-                (ty::ReEarlyBound(_), ty::ReEarlyBound(_))
-                | (ty::ReFree(_), ty::ReEarlyBound(_))
-                | (ty::ReEarlyBound(_), ty::ReFree(_))
-                | (ty::ReFree(_), ty::ReFree(_)) => {
-                    self.free_regions.sub_free_regions(sub_region, super_region)
-                }
-
-                _ => false,
-            }
-        };
-        let result = result || self.is_static(super_region);
-        debug!(
-            "is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
-            sub_region, super_region, result
-        );
-        result
-    }
-
-    /// Determines whether this free region is required to be `'static`.
-    fn is_static(&self, super_region: ty::Region<'tcx>) -> bool {
-        debug!("is_static(super_region={:?})", super_region);
-        match *super_region {
-            ty::ReStatic => true,
-            ty::ReEarlyBound(_) | ty::ReFree(_) => {
-                let re_static = self.tcx.mk_region(ty::ReStatic);
-                self.free_regions.sub_free_regions(&re_static, &super_region)
-            }
-            _ => false,
-        }
-    }
-
     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)
     }
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs
index 29ea47809a0..855da0367de 100644
--- a/src/librustc/traits/coherence.rs
+++ b/src/librustc/traits/coherence.rs
@@ -7,6 +7,7 @@
 use crate::infer::{CombinedSnapshot, InferOk};
 use crate::traits::select::IntercrateAmbiguityCause;
 use crate::traits::IntercrateMode;
+use crate::traits::SkipLeakCheck;
 use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext};
 use crate::ty::fold::TypeFoldable;
 use crate::ty::subst::Subst;
@@ -53,6 +54,7 @@ pub fn overlapping_impls<F1, F2, R>(
     impl1_def_id: DefId,
     impl2_def_id: DefId,
     intercrate_mode: IntercrateMode,
+    skip_leak_check: SkipLeakCheck,
     on_overlap: F1,
     no_overlap: F2,
 ) -> R
@@ -70,7 +72,7 @@ where
 
     let overlaps = tcx.infer_ctxt().enter(|infcx| {
         let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode);
-        overlap(selcx, impl1_def_id, impl2_def_id).is_some()
+        overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some()
     });
 
     if !overlaps {
@@ -83,7 +85,7 @@ where
     tcx.infer_ctxt().enter(|infcx| {
         let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode);
         selcx.enable_tracking_intercrate_ambiguity_causes();
-        on_overlap(overlap(selcx, impl1_def_id, impl2_def_id).unwrap())
+        on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap())
     })
 }
 
@@ -113,12 +115,15 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
 /// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
 fn overlap<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
+    skip_leak_check: SkipLeakCheck,
     a_def_id: DefId,
     b_def_id: DefId,
 ) -> Option<OverlapResult<'tcx>> {
     debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id);
 
-    selcx.infcx().probe(|snapshot| overlap_within_probe(selcx, a_def_id, b_def_id, snapshot))
+    selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
+        overlap_within_probe(selcx, a_def_id, b_def_id, snapshot)
+    })
 }
 
 fn overlap_within_probe(
@@ -146,7 +151,9 @@ fn overlap_within_probe(
         .eq_impl_headers(&a_impl_header, &b_impl_header)
     {
         Ok(InferOk { obligations, value: () }) => obligations,
-        Err(_) => return None,
+        Err(_) => {
+            return None;
+        }
     };
 
     debug!("overlap: unification check succeeded");
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index e88f4e65c7e..50068b89687 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -83,6 +83,28 @@ pub enum IntercrateMode {
     Fixed,
 }
 
+/// Whether to skip the leak check, as part of a future compatibility warning step.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum SkipLeakCheck {
+    Yes,
+    No,
+}
+
+impl SkipLeakCheck {
+    fn is_yes(self) -> bool {
+        self == SkipLeakCheck::Yes
+    }
+}
+
+/// The "default" for skip-leak-check corresponds to the current
+/// behavior (do not skip the leak check) -- not the behavior we are
+/// transitioning into.
+impl Default for SkipLeakCheck {
+    fn default() -> Self {
+        SkipLeakCheck::No
+    }
+}
+
 /// The mode that trait queries run in.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum TraitQueryMode {
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index e559ea391cd..8b68d6f2603 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -19,6 +19,7 @@ use crate::ty::{self, TyCtxt, TypeFoldable};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
 use rustc_hir::def_id::DefId;
+use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
 use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
 use rustc_span::DUMMY_SP;
 
@@ -97,7 +98,7 @@ pub fn translate_substs<'a, 'tcx>(
                 |_| {
                     bug!(
                         "When translating substitutions for specialization, the expected \
-                          specialization failed to hold"
+                         specialization failed to hold"
                     )
                 },
             )
@@ -268,7 +269,7 @@ fn fulfill_implication<'a, 'tcx>(
                 // no dice!
                 debug!(
                     "fulfill_implication: for impls on {:?} and {:?}, \
-                        could not fulfill: {:?} given {:?}",
+                     could not fulfill: {:?} given {:?}",
                     source_trait_ref, target_trait_ref, errors, param_env.caller_bounds
                 );
                 Err(())
@@ -342,6 +343,7 @@ pub(super) fn specialization_graph_provider(
                             FutureCompatOverlapErrorKind::Issue33140 => {
                                 ORDER_DEPENDENT_TRAIT_OBJECTS
                             }
+                            FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK,
                         };
                         tcx.struct_span_lint_hir(
                             lint,
diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs
index c90fa428001..98908e672f0 100644
--- a/src/librustc/traits/specialize/specialization_graph.rs
+++ b/src/librustc/traits/specialize/specialization_graph.rs
@@ -11,6 +11,7 @@ pub use rustc::traits::types::specialization_graph::*;
 pub enum FutureCompatOverlapErrorKind {
     Issue43355,
     Issue33140,
+    LeakCheck,
 }
 
 #[derive(Debug)]
@@ -111,6 +112,7 @@ impl<'tcx> Children {
                 possible_sibling,
                 impl_def_id,
                 traits::IntercrateMode::Issue43355,
+                traits::SkipLeakCheck::default(),
                 |overlap| {
                     if let Some(overlap_kind) =
                         tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling)
@@ -161,6 +163,7 @@ impl<'tcx> Children {
                         possible_sibling,
                         impl_def_id,
                         traits::IntercrateMode::Fixed,
+                        traits::SkipLeakCheck::default(),
                         |overlap| {
                             last_lint = Some(FutureCompatOverlapError {
                                 error: overlap_error(overlap),
@@ -169,6 +172,23 @@ impl<'tcx> Children {
                         },
                         || (),
                     );
+
+                    if last_lint.is_none() {
+                        traits::overlapping_impls(
+                            tcx,
+                            possible_sibling,
+                            impl_def_id,
+                            traits::IntercrateMode::Fixed,
+                            traits::SkipLeakCheck::Yes,
+                            |overlap| {
+                                last_lint = Some(FutureCompatOverlapError {
+                                    error: overlap_error(overlap),
+                                    kind: FutureCompatOverlapErrorKind::LeakCheck,
+                                });
+                            },
+                            || (),
+                        );
+                    }
                 }
 
                 // no overlap (error bailed already via ?)
@@ -247,7 +267,7 @@ impl<'tcx> Graph {
         if trait_ref.references_error() {
             debug!(
                 "insert: inserting dummy node for erroneous TraitRef {:?}, \
-                    impl_def_id={:?}, trait_def_id={:?}",
+                 impl_def_id={:?}, trait_def_id={:?}",
                 trait_ref, impl_def_id, trait_def_id
             );
 
@@ -326,7 +346,7 @@ impl<'tcx> Graph {
         if self.parent.insert(child, parent).is_some() {
             bug!(
                 "When recording an impl from the crate store, information about its parent \
-                  was already present."
+                 was already present."
             );
         }
 
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 8386058f72a..92c5600362e 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -173,8 +173,13 @@ pub struct CommonTypes<'tcx> {
 }
 
 pub struct CommonLifetimes<'tcx> {
-    pub re_empty: Region<'tcx>,
+    /// `ReEmpty` in the root universe.
+    pub re_root_empty: Region<'tcx>,
+
+    /// `ReStatic`
     pub re_static: Region<'tcx>,
+
+    /// Erased region, used after type-checking
     pub re_erased: Region<'tcx>,
 }
 
@@ -876,7 +881,7 @@ impl<'tcx> CommonLifetimes<'tcx> {
         let mk = |r| interners.region.intern(r, |r| Interned(interners.arena.alloc(r))).0;
 
         CommonLifetimes {
-            re_empty: mk(RegionKind::ReEmpty),
+            re_root_empty: mk(RegionKind::ReEmpty(ty::UniverseIndex::ROOT)),
             re_static: mk(RegionKind::ReStatic),
             re_erased: mk(RegionKind::ReErased),
         }
diff --git a/src/librustc/ty/free_region_map.rs b/src/librustc/ty/free_region_map.rs
index 42f506606e6..2ab12a4acbf 100644
--- a/src/librustc/ty/free_region_map.rs
+++ b/src/librustc/ty/free_region_map.rs
@@ -23,11 +23,61 @@ impl<'tcx> FreeRegionMap<'tcx> {
     // (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 is_free_or_static(sub) && is_free(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 {
+        match *r {
+            ty::ReEarlyBound(_) | ty::ReFree(_) => true,
+            _ => false,
+        }
+    }
+
+    /// 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
@@ -39,13 +89,13 @@ impl<'tcx> FreeRegionMap<'tcx> {
         r_b: Region<'tcx>,
     ) -> Region<'tcx> {
         debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
-        assert!(is_free(r_a));
-        assert!(is_free(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.mk_region(ty::ReStatic),
+                None => tcx.lifetimes.re_static,
                 Some(r) => *r,
             }
         };
@@ -60,31 +110,18 @@ impl<'tcx> FreeRegionMap<'tcx> {
 pub trait FreeRegionRelations<'tcx> {
     /// Tests whether `r_a <= r_b`. Both must be free regions or
     /// `'static`.
-    fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
+    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, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
-        assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
-        if let ty::ReStatic = r_b {
-            true // `'a <= 'static` is just always true, and not stored in the relation explicitly
-        } else {
-            r_a == r_b || self.relation.contains(&r_a, &r_b)
-        }
-    }
-}
-
-fn is_free(r: Region<'_>) -> bool {
-    match *r {
-        ty::ReEarlyBound(_) | ty::ReFree(_) => true,
-        _ => false,
-    }
-}
-
-fn is_free_or_static(r: Region<'_>) -> bool {
-    match *r {
-        ty::ReStatic => true,
-        _ => is_free(r),
+    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)
     }
 }
 
diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs
index f5c14e73db2..0da680d1f91 100644
--- a/src/librustc/ty/print/pretty.rs
+++ b/src/librustc/ty/print/pretty.rs
@@ -1382,7 +1382,7 @@ impl<F: fmt::Write> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> {
 
             ty::ReVar(_) | ty::ReScope(_) | ty::ReErased => false,
 
-            ty::ReStatic | ty::ReEmpty | ty::ReClosureBound(_) => true,
+            ty::ReStatic | ty::ReEmpty(_) | ty::ReClosureBound(_) => true,
         }
     }
 }
@@ -1464,10 +1464,14 @@ impl<F: fmt::Write> FmtPrinter<'_, '_, F> {
                 p!(write("'static"));
                 return Ok(self);
             }
-            ty::ReEmpty => {
+            ty::ReEmpty(ty::UniverseIndex::ROOT) => {
                 p!(write("'<empty>"));
                 return Ok(self);
             }
+            ty::ReEmpty(ui) => {
+                p!(write("'<empty:{:?}>", ui));
+                return Ok(self);
+            }
 
             // The user should never encounter these in unsubstituted form.
             ty::ReClosureBound(vid) => {
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 9d00d272263..acd6c959751 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -108,7 +108,7 @@ impl fmt::Debug for ty::RegionKind {
 
             ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder),
 
-            ty::ReEmpty => write!(f, "ReEmpty"),
+            ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui),
 
             ty::ReErased => write!(f, "ReErased"),
         }
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 0d30395d250..4c5bc3debde 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -1292,11 +1292,67 @@ rustc_index::newtype_index! {
 
 pub type Region<'tcx> = &'tcx RegionKind;
 
-/// Representation of regions.
+/// Representation of (lexical) regions. Note that the NLL checker
+/// uses a distinct representation of regions. For this reason, it
+/// internally replaces all the regions with inference variables --
+/// the index of the variable is then used to index into internal NLL
+/// data structures. See `rustc_mir::borrow_check` module for more
+/// information.
 ///
-/// Unlike types, most region variants are "fictitious", not concrete,
-/// regions. Among these, `ReStatic`, `ReEmpty` and `ReScope` are the only
-/// ones representing concrete regions.
+/// ## The Region lattice within a given function
+///
+/// In general, the (lexical, and hence deprecated) region lattice
+/// looks like
+///
+/// ```
+/// static ----------+-----...------+       (greatest)
+/// |                |              |
+/// early-bound and  |              |
+/// free regions     |              |
+/// |                |              |
+/// scope regions    |              |
+/// |                |              |
+/// empty(root)   placeholder(U1)   |
+/// |            /                  |
+/// |           /         placeholder(Un)
+/// empty(U1) --         /
+/// |                   /
+/// ...                /
+/// |                 /
+/// empty(Un) --------                      (smallest)
+/// ```
+///
+/// Early-bound/free regions are the named lifetimes in scope from the
+/// function declaration. They have relationships to one another
+/// determined based on the declared relationships from the
+/// function. They all collectively outlive the scope regions. (See
+/// `RegionRelations` type, and particularly
+/// `crate::infer::outlives::free_region_map::FreeRegionMap`.)
+///
+/// The scope regions are related to one another based on the AST
+/// structure. (See `RegionRelations` type, and particularly the
+/// `rustc::middle::region::ScopeTree`.)
+///
+/// Note that inference variables and bound regions are not included
+/// in this diagram. In the case of inference variables, they should
+/// be inferred to some other region from the diagram.  In the case of
+/// bound regions, they are excluded because they don't make sense to
+/// include -- the diagram indicates the relationship between free
+/// regions.
+///
+/// ## Inference variables
+///
+/// During region inference, we sometimes create inference variables,
+/// represented as `ReVar`. These will be inferred by the code in
+/// `infer::lexical_region_resolve` to some free region from the
+/// lattice above (the minimal region that meets the
+/// constraints).
+///
+/// During NLL checking, where regions are defined differently, we
+/// also use `ReVar` -- in that case, the index is used to index into
+/// the NLL region checker's data structures. The variable may in fact
+/// represent either a free region or an inference variable, in that
+/// case.
 ///
 /// ## Bound Regions
 ///
@@ -1379,14 +1435,13 @@ pub enum RegionKind {
     /// Should not exist after typeck.
     RePlaceholder(ty::PlaceholderRegion),
 
-    /// Empty lifetime is for data that is never accessed.
-    /// Bottom in the region lattice. We treat ReEmpty somewhat
-    /// specially; at least right now, we do not generate instances of
-    /// it during the GLB computations, but rather
-    /// generate an error instead. This is to improve error messages.
-    /// The only way to get an instance of ReEmpty is to have a region
-    /// variable with no constraints.
-    ReEmpty,
+    /// Empty lifetime is for data that is never accessed.  We tag the
+    /// empty lifetime with a universe -- the idea is that we don't
+    /// want `exists<'a> { forall<'b> { 'b: 'a } }` to be satisfiable.
+    /// Therefore, the `'empty` in a universe `U` is less than all
+    /// regions visible from `U`, but not less than regions not visible
+    /// from `U`.
+    ReEmpty(ty::UniverseIndex),
 
     /// Erased region, used by trait selection, in MIR and during codegen.
     ReErased,
@@ -1635,7 +1690,7 @@ impl RegionKind {
             RegionKind::ReStatic => true,
             RegionKind::ReVar(..) => false,
             RegionKind::RePlaceholder(placeholder) => placeholder.name.is_named(),
-            RegionKind::ReEmpty => false,
+            RegionKind::ReEmpty(_) => false,
             RegionKind::ReErased => false,
             RegionKind::ReClosureBound(..) => false,
         }
@@ -1718,7 +1773,7 @@ impl RegionKind {
                 flags = flags | TypeFlags::HAS_FREE_REGIONS;
                 flags = flags | TypeFlags::HAS_RE_EARLY_BOUND;
             }
-            ty::ReEmpty | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => {
+            ty::ReEmpty(_) | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => {
                 flags = flags | TypeFlags::HAS_FREE_REGIONS;
             }
             ty::ReErased => {}
@@ -1728,7 +1783,7 @@ impl RegionKind {
         }
 
         match *self {
-            ty::ReStatic | ty::ReEmpty | ty::ReErased | ty::ReLateBound(..) => (),
+            ty::ReStatic | ty::ReEmpty(_) | ty::ReErased | ty::ReLateBound(..) => (),
             _ => flags = flags | TypeFlags::HAS_FREE_LOCAL_NAMES,
         }
 
diff --git a/src/librustc_index/vec.rs b/src/librustc_index/vec.rs
index d14bafb44fd..1dfe97238a3 100644
--- a/src/librustc_index/vec.rs
+++ b/src/librustc_index/vec.rs
@@ -574,6 +574,14 @@ impl<I: Idx, T> IndexVec<I, T> {
         IndexVec { raw: vec![elem; n], _marker: PhantomData }
     }
 
+    /// Create an `IndexVec` with `n` elements, where the value of each
+    /// element is the result of `func(i)`
+    #[inline]
+    pub fn from_fn_n(func: impl FnMut(I) -> T, n: usize) -> Self {
+        let indices = (0..n).map(I::new);
+        Self::from_raw(indices.map(func).collect())
+    }
+
     #[inline]
     pub fn push(&mut self, d: T) -> I {
         let idx = I::new(self.len());
diff --git a/src/librustc_mir/borrow_check/diagnostics/region_name.rs b/src/librustc_mir/borrow_check/diagnostics/region_name.rs
index 47eb2d8940a..09d61d9ad9a 100644
--- a/src/librustc_mir/borrow_check/diagnostics/region_name.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/region_name.rs
@@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
             | ty::ReScope(..)
             | ty::ReVar(..)
             | ty::RePlaceholder(..)
-            | ty::ReEmpty
+            | ty::ReEmpty(_)
             | ty::ReErased
             | ty::ReClosureBound(..) => None,
         }
diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs
index 26d9cf2e045..6abca481eac 100644
--- a/src/librustc_mir/borrow_check/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/region_infer/mod.rs
@@ -1108,6 +1108,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 self.eval_if_eq(tcx, body, generic_ty, lower_bound, test_ty, verify_bound1)
             }
 
+            VerifyBound::IsEmpty => {
+                let lower_bound_scc = self.constraint_sccs.scc(lower_bound);
+                self.scc_values.elements_contained_in(lower_bound_scc).next().is_none()
+            }
+
             VerifyBound::OutlivedBy(r) => {
                 let r_vid = self.to_region_vid(r);
                 self.eval_outlives(r_vid, lower_bound)
diff --git a/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs
index 8f65a0f01c6..a3e38cd7a5f 100644
--- a/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs
+++ b/src/librustc_mir/borrow_check/type_check/constraint_conversion.rs
@@ -160,7 +160,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
         a: ty::Region<'tcx>,
         b: ty::Region<'tcx>,
     ) {
-        if let ty::ReEmpty = a {
+        // FIXME -- this is not the fix I would prefer
+        if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a {
             return;
         }
         let b = self.to_region_vid(b);
@@ -175,7 +176,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
         a: ty::Region<'tcx>,
         bound: VerifyBound<'tcx>,
     ) {
-        if let ty::ReEmpty = a {
+        // FIXME: I'd prefer if NLL had a notion of empty
+        if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a {
             return;
         }
         let type_test = self.verify_to_type_test(kind, a, bound);
diff --git a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs
index f0dc94f417c..cf8c3449d66 100644
--- a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs
+++ b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs
@@ -5,7 +5,7 @@ use rustc::mir::ConstraintCategory;
 use rustc::traits::query::outlives_bounds::{self, OutlivesBound};
 use rustc::traits::query::type_op::{self, TypeOp};
 use rustc::ty::free_region_map::FreeRegionRelations;
-use rustc::ty::{self, RegionVid, Ty};
+use rustc::ty::{self, RegionVid, Ty, TyCtxt};
 use rustc_data_structures::transitive_relation::TransitiveRelation;
 use rustc_span::DUMMY_SP;
 use std::rc::Rc;
@@ -333,7 +333,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
                     // `where Type:` is lowered to `where Type: 'empty` so that
                     // we check `Type` is well formed, but there's no use for
                     // this bound here.
-                    if let ty::ReEmpty = r1 {
+                    if let ty::ReEmpty(_) = r1 {
                         return;
                     }
 
@@ -359,7 +359,12 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
 /// over the `FreeRegionMap` from lexical regions and
 /// `UniversalRegions` (from NLL)`.
 impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegionRelations<'tcx> {
-    fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool {
+    fn sub_free_regions(
+        &self,
+        _tcx: TyCtxt<'tcx>,
+        shorter: ty::Region<'tcx>,
+        longer: ty::Region<'tcx>,
+    ) -> bool {
         let shorter = shorter.to_region_vid();
         assert!(self.universal_regions.is_universal_region(shorter));
         let longer = longer.to_region_vid();
diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs
index a61ab5b5e17..5a360b40d61 100644
--- a/src/librustc_session/lint/builtin.rs
+++ b/src/librustc_session/lint/builtin.rs
@@ -261,6 +261,16 @@ declare_lint! {
 }
 
 declare_lint! {
+    pub COHERENCE_LEAK_CHECK,
+    Warn,
+    "distinct impls distinguished only by the leak-check code",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #56105 <https://github.com/rust-lang/rust/issues/56105>",
+        edition: None,
+    };
+}
+
+declare_lint! {
     pub DEPRECATED,
     Warn,
     "detects use of deprecated items",
@@ -515,6 +525,7 @@ declare_lint_pass! {
         MISSING_FRAGMENT_SPECIFIER,
         LATE_BOUND_LIFETIME_ARGUMENTS,
         ORDER_DEPENDENT_TRAIT_OBJECTS,
+        COHERENCE_LEAK_CHECK,
         DEPRECATED,
         UNUSED_UNSAFE,
         UNUSED_MUT,
diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs
index 70e3c4c7ab1..301ebf8adc5 100644
--- a/src/librustc_traits/chalk_context/resolvent_ops.rs
+++ b/src/librustc_traits/chalk_context/resolvent_ops.rs
@@ -246,9 +246,11 @@ impl TypeRelation<'tcx> for AnswerSubstitutor<'cx, 'tcx> {
                 assert_eq!(a_bound.assert_bound_var(), b_bound.assert_bound_var());
             }
 
-            (ty::ReStatic, ty::ReStatic)
-            | (ty::ReErased, ty::ReErased)
-            | (ty::ReEmpty, ty::ReEmpty) => (),
+            (ty::ReStatic, ty::ReStatic) | (ty::ReErased, ty::ReErased) => (),
+
+            (ty::ReEmpty(a_ui), ty::ReEmpty(b_ui)) => {
+                assert_eq!(a_ui, b_ui);
+            }
 
             (&ty::ReFree(a_free), &ty::ReFree(b_free)) => {
                 assert_eq!(a_free, b_free);
diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
index d60c3cfba9a..3e17b661cf4 100644
--- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs
+++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
@@ -1,5 +1,5 @@
 use crate::namespace::Namespace;
-use rustc::traits::{self, IntercrateMode};
+use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
 use rustc::ty::TyCtxt;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
@@ -76,6 +76,9 @@ impl InherentOverlapChecker<'tcx> {
                     impl1_def_id,
                     impl2_def_id,
                     IntercrateMode::Issue43355,
+                    // We go ahead and just skip the leak check for
+                    // inherent impls without warning.
+                    SkipLeakCheck::Yes,
                     |overlap| {
                         self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
                         false
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 040b85b98ed..2a450f4b4e8 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -2324,7 +2324,8 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
                         // compiler/tooling bugs from not handling WF predicates.
                     } else {
                         let span = bound_pred.bounded_ty.span;
-                        let predicate = ty::OutlivesPredicate(ty, tcx.mk_region(ty::ReEmpty));
+                        let re_root_empty = tcx.lifetimes.re_root_empty;
+                        let predicate = ty::OutlivesPredicate(ty, re_root_empty);
                         predicates.push((
                             ty::Predicate::TypeOutlives(ty::Binder::dummy(predicate)),
                             span,
diff --git a/src/librustc_typeck/outlives/utils.rs b/src/librustc_typeck/outlives/utils.rs
index 2d7fc9d62e5..0cc322f8c2d 100644
--- a/src/librustc_typeck/outlives/utils.rs
+++ b/src/librustc_typeck/outlives/utils.rs
@@ -166,7 +166,7 @@ fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool {
         //
         //     struct Bar<T>(<Self as Foo>::Type) where Self: ;
         //     struct Baz<'a>(&'a Self) where Self: ;
-        RegionKind::ReEmpty => false,
+        RegionKind::ReEmpty(_) => false,
 
         // These regions don't appear in types from type declarations:
         RegionKind::ReErased
diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs
index 5ca961ed344..6f5caea250b 100644
--- a/src/librustc_typeck/variance/constraints.rs
+++ b/src/librustc_typeck/variance/constraints.rs
@@ -453,7 +453,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
             | ty::ReScope(..)
             | ty::ReVar(..)
             | ty::RePlaceholder(..)
-            | ty::ReEmpty
+            | ty::ReEmpty(_)
             | ty::ReErased => {
                 // We don't expect to see anything but 'static or bound
                 // regions when visiting member types or method types.
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2a35ab812a5..f140f11b090 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -447,7 +447,7 @@ impl Clean<Option<Lifetime>> for ty::RegionKind {
             | ty::ReScope(..)
             | ty::ReVar(..)
             | ty::RePlaceholder(..)
-            | ty::ReEmpty
+            | ty::ReEmpty(_)
             | ty::ReClosureBound(_)
             | ty::ReErased => {
                 debug!("cannot clean region {:?}", self);
@@ -521,7 +521,7 @@ impl<'tcx> Clean<Option<WherePredicate>>
         let ty::OutlivesPredicate(ref a, ref b) = *self;
 
         match (a, b) {
-            (ty::ReEmpty, ty::ReEmpty) => {
+            (ty::ReEmpty(_), ty::ReEmpty(_)) => {
                 return None;
             }
             _ => {}
@@ -539,7 +539,7 @@ impl<'tcx> Clean<Option<WherePredicate>> for ty::OutlivesPredicate<Ty<'tcx>, ty:
         let ty::OutlivesPredicate(ref ty, ref lt) = *self;
 
         match lt {
-            ty::ReEmpty => return None,
+            ty::ReEmpty(_) => return None,
             _ => {}
         }
 
diff --git a/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr b/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr
new file mode 100644
index 00000000000..6ea0b89be74
--- /dev/null
+++ b/src/test/ui/coherence/coherence-inherited-subtyping.old.stderr
@@ -0,0 +1,14 @@
+error[E0592]: duplicate definitions with name `method1`
+  --> $DIR/coherence-inherited-subtyping.rs:14:5
+   |
+LL |     fn method1(&self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1`
+...
+LL |     fn method1(&self) {}
+   |     -------------------- other definition for `method1`
+   |
+   = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0592`.
diff --git a/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr b/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr
new file mode 100644
index 00000000000..6ea0b89be74
--- /dev/null
+++ b/src/test/ui/coherence/coherence-inherited-subtyping.re.stderr
@@ -0,0 +1,14 @@
+error[E0592]: duplicate definitions with name `method1`
+  --> $DIR/coherence-inherited-subtyping.rs:14:5
+   |
+LL |     fn method1(&self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1`
+...
+LL |     fn method1(&self) {}
+   |     -------------------- other definition for `method1`
+   |
+   = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0592`.
diff --git a/src/test/ui/coherence/coherence-inherited-subtyping.rs b/src/test/ui/coherence/coherence-inherited-subtyping.rs
new file mode 100644
index 00000000000..8587eb77950
--- /dev/null
+++ b/src/test/ui/coherence/coherence-inherited-subtyping.rs
@@ -0,0 +1,21 @@
+// Test that two distinct impls which match subtypes of one another
+// yield coherence errors (or not) depending on the variance.
+//
+// Note: This scenario is currently accepted, but as part of the
+// universe transition (#56105) may eventually become an error.
+
+// revisions: old re
+
+struct Foo<T> {
+    t: T,
+}
+
+impl Foo<for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8> {
+    fn method1(&self) {} //~ ERROR duplicate definitions with name `method1`
+}
+
+impl Foo<for<'a> fn(&'a u8, &'a u8) -> &'a u8> {
+    fn method1(&self) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/coherence/coherence-subtyping.old.stderr b/src/test/ui/coherence/coherence-subtyping.old.stderr
new file mode 100644
index 00000000000..76f5cc1b782
--- /dev/null
+++ b/src/test/ui/coherence/coherence-subtyping.old.stderr
@@ -0,0 +1,14 @@
+warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`:
+  --> $DIR/coherence-subtyping.rs:16:1
+   |
+LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
+   | ---------------------------------------------------------- first implementation here
+LL | 
+LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
+   |
+   = note: `#[warn(coherence_leak_check)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
+   = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
+
diff --git a/src/test/ui/coherence/coherence-subtyping.re.stderr b/src/test/ui/coherence/coherence-subtyping.re.stderr
new file mode 100644
index 00000000000..76f5cc1b782
--- /dev/null
+++ b/src/test/ui/coherence/coherence-subtyping.re.stderr
@@ -0,0 +1,14 @@
+warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`:
+  --> $DIR/coherence-subtyping.rs:16:1
+   |
+LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
+   | ---------------------------------------------------------- first implementation here
+LL | 
+LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
+   |
+   = note: `#[warn(coherence_leak_check)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
+   = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
+
diff --git a/src/test/ui/coherence/coherence-subtyping.rs b/src/test/ui/coherence/coherence-subtyping.rs
index a742bf2884e..f5c1d92411b 100644
--- a/src/test/ui/coherence/coherence-subtyping.rs
+++ b/src/test/ui/coherence/coherence-subtyping.rs
@@ -5,16 +5,19 @@
 // universe transition (#56105) may eventually become an error.
 
 // revisions: old re
-// build-pass (FIXME(62277): could be check-pass?)
+// check-pass
 
 trait TheTrait {
-    fn foo(&self) { }
+    fn foo(&self) {}
 }
 
-impl TheTrait for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
-}
+impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
 
 impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
+    //[re]~^ WARNING conflicting implementation
+    //[re]~^^ WARNING this was previously accepted by the compiler but is being phased out
+    //[old]~^^^ WARNING conflicting implementation
+    //[old]~^^^^ WARNING this was previously accepted by the compiler but is being phased out
 }
 
-fn main() { }
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs
index 20edfb33931..2f18f600b75 100644
--- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs
+++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs
@@ -36,7 +36,7 @@ where
     T: Anything<'b, 'c>,
 {
     with_signature(cell, t, |cell, t| require(cell, t));
-    //~^ ERROR associated type `<T as Anything<'_#5r, '_#6r>>::AssocType` may not live long enough
+    //~^ ERROR may not live long enough
 }
 
 #[rustc_regions]
@@ -46,7 +46,7 @@ where
     'a: 'a,
 {
     with_signature(cell, t, |cell, t| require(cell, t));
-    //~^ ERROR associated type `<T as Anything<'_#6r, '_#7r>>::AssocType` may not live long enough
+    //~^ ERROR may not live long enough
 }
 
 #[rustc_regions]