about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-01-03 17:18:15 +0000
committerbors <bors@rust-lang.org>2019-01-03 17:18:15 +0000
commitc0bbc3927e28c22edefe6a1353b5ecc95ea9a104 (patch)
tree2cc354e8390966e438b510b0ba376b7fa14927b5
parent2442823ef572a65092fbc46f6975633f983b50b6 (diff)
parent8e89184a7beb03d58d3a3969dddc1d78964dec37 (diff)
Auto merge of #55517 - nikomatsakis:universes, r=scalexm
Universes

This PR transitions the compiler to use **universes** instead of the **leak-check**. It is marked as [WIP] for a few reasons:

- The diagnostics at present are terrible =)
- This changes the behavior of coherence, regressing some things that used to compile

The goals of this PR at present are:

- To start getting some eyes on the code
- To do a crater run
- To see the full travis results (due to https://github.com/rust-lang/rust/issues/52452, I am not able to run the full test suite locally anymore at present)

The first few commits in the PR are changing how `evaluate` treats regions. We now track whether region comparisons occurred, reverting the "staticized" query approach that @arielb1 put in. The problem with "staticized" queries is that it relied on the leak-check to get higher-ranked things correct, and we are removing the leak-check in this PR series, so that caused problems.

You can see at the end a collection of test updates. Mostly we behave the same but with atrocious diagnostics, but there are a number of cases where we used to error and now no longer do, as well as single case where we used to **not** error but we now do (the coherence-subtyping change).

(Note: it would be possible to do a version of leak-check that propagates universe information and recover the old behavior. I am reluctant to do so because I'd like to leave us room to get more precise -- e.g., I want to eventually handle things like `exists<'a> { for<'b> { if ('a: 'b) { 'a: 'b } } }` which presently the leak-check cannot cope with etc. Also because it seems more consistent to me: most folks I've talked to expect the new behavior and are surprised to learn that binding sites were so significant before when it comes to coherence. One question is, though, to what extent are people relying on this in the wild?)
-rw-r--r--src/librustc/infer/error_reporting/mod.rs60
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/mod.rs8
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs3
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs307
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs1
-rw-r--r--src/librustc/infer/error_reporting/note.rs20
-rw-r--r--src/librustc/infer/higher_ranked/mod.rs507
-rw-r--r--src/librustc/infer/lexical_region_resolve/mod.rs49
-rw-r--r--src/librustc/infer/mod.rs109
-rw-r--r--src/librustc/infer/region_constraints/mod.rs61
-rw-r--r--src/librustc/infer/region_constraints/taint.rs85
-rw-r--r--src/librustc/middle/expr_use_visitor.rs2
-rw-r--r--src/librustc/middle/mem_categorization.rs15
-rw-r--r--src/librustc/traits/auto_trait.rs8
-rw-r--r--src/librustc/traits/coherence.rs39
-rw-r--r--src/librustc/traits/error_reporting.rs9
-rw-r--r--src/librustc/traits/fulfill.rs8
-rw-r--r--src/librustc/traits/mod.rs25
-rw-r--r--src/librustc/traits/object_safety.rs2
-rw-r--r--src/librustc/traits/project.rs48
-rw-r--r--src/librustc/traits/query/evaluate_obligation.rs19
-rw-r--r--src/librustc/traits/select.rs322
-rw-r--r--src/librustc/traits/specialize/mod.rs6
-rw-r--r--src/librustc/traits/specialize/specialization_graph.rs1
-rw-r--r--src/librustc/ty/error.rs20
-rw-r--r--src/librustc/ty/query/mod.rs2
-rw-r--r--src/librustc/ty/structural_impls.rs10
-rw-r--r--src/librustc/ty/sty.rs7
-rw-r--r--src/librustc/ty/util.rs74
-rw-r--r--src/librustc/util/ppaux.rs252
-rw-r--r--src/librustc_driver/test.rs51
-rw-r--r--src/librustc_lint/builtin.rs2
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs10
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs9
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs18
-rw-r--r--src/librustc_mir/build/misc.rs2
-rw-r--r--src/librustc_mir/hair/cx/mod.rs4
-rw-r--r--src/librustc_mir/hair/pattern/check_match.rs2
-rw-r--r--src/librustc_mir/shim.rs2
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs7
-rw-r--r--src/librustc_typeck/check/cast.rs8
-rw-r--r--src/librustc_typeck/check/demand.rs6
-rw-r--r--src/librustc_typeck/check/op.rs12
-rw-r--r--src/librustc_typeck/check/wfcheck.rs2
-rw-r--r--src/librustc_typeck/coherence/inherent_impls_overlap.rs4
-rw-r--r--src/test/run-pass/coherence/coherence-subtyping.rs39
-rw-r--r--src/test/ui/associated-types/associated-types-eq-hr.rs22
-rw-r--r--src/test/ui/associated-types/associated-types-eq-hr.stderr99
-rw-r--r--src/test/ui/associated-types/higher-ranked-projection.bad.stderr15
-rw-r--r--src/test/ui/associated-types/higher-ranked-projection.good.stderr3
-rw-r--r--src/test/ui/associated-types/higher-ranked-projection.rs3
-rw-r--r--src/test/ui/closure-expected-type/expect-fn-supply-fn.rs9
-rw-r--r--src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr59
-rw-r--r--src/test/ui/coherence/coherence-subtyping.rs15
-rw-r--r--src/test/ui/coherence/coherence-subtyping.stderr14
-rw-r--r--src/test/ui/generator/auto-trait-regions.rs6
-rw-r--r--src/test/ui/generator/auto-trait-regions.stderr37
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr2
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr22
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr6
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr6
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr2
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr22
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr22
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr6
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr22
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr2
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr6
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr6
-rw-r--r--src/test/ui/hr-subtype/hr-subtype.rs20
-rw-r--r--src/test/ui/hrtb/hrtb-cache-issue-54302.rs24
-rw-r--r--src/test/ui/hrtb/hrtb-cache-issue-54302.stderr11
-rw-r--r--src/test/ui/hrtb/hrtb-conflate-regions.rs2
-rw-r--r--src/test/ui/hrtb/hrtb-conflate-regions.stderr20
-rw-r--r--src/test/ui/hrtb/hrtb-exists-forall-fn.rs18
-rw-r--r--src/test/ui/hrtb/hrtb-exists-forall-fn.stderr12
-rw-r--r--src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.rs35
-rw-r--r--src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.rs37
-rw-r--r--src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.rs29
-rw-r--r--src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr12
-rw-r--r--src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.rs2
-rw-r--r--src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr19
-rw-r--r--src/test/ui/hrtb/hrtb-higher-ranker-supertraits.rs4
-rw-r--r--src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr41
-rw-r--r--src/test/ui/hrtb/hrtb-just-for-static.rs8
-rw-r--r--src/test/ui/hrtb/hrtb-just-for-static.stderr30
-rw-r--r--src/test/ui/hrtb/hrtb-perfect-forwarding.rs2
-rw-r--r--src/test/ui/hrtb/hrtb-perfect-forwarding.stderr23
-rw-r--r--src/test/ui/issues/issue-22872.rs1
-rw-r--r--src/test/ui/issues/issue-22872.stderr19
-rw-r--r--src/test/ui/issues/issue-40000.rs3
-rw-r--r--src/test/ui/issues/issue-40000.stderr8
-rw-r--r--src/test/ui/issues/issue-54302-cases.rs8
-rw-r--r--src/test/ui/issues/issue-54302-cases.stderr57
-rw-r--r--src/test/ui/issues/issue-54302.rs2
-rw-r--r--src/test/ui/issues/issue-54302.stderr12
-rw-r--r--src/test/ui/lub-glb/old-lub-glb-hr.rs9
-rw-r--r--src/test/ui/lub-glb/old-lub-glb-hr.stderr17
-rw-r--r--src/test/ui/lub-glb/old-lub-glb-object.rs2
-rw-r--r--src/test/ui/lub-glb/old-lub-glb-object.stderr11
-rw-r--r--src/test/ui/mismatched_types/closure-arg-type-mismatch.rs4
-rw-r--r--src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr33
-rw-r--r--src/test/ui/mismatched_types/closure-mismatch.rs3
-rw-r--r--src/test/ui/mismatched_types/closure-mismatch.stderr34
-rw-r--r--src/test/ui/regions-fn-subtyping-return-static-fail.rs9
-rw-r--r--src/test/ui/regions-fn-subtyping-return-static-fail.stderr19
-rw-r--r--src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr4
-rw-r--r--src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs2
-rw-r--r--src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr6
-rw-r--r--src/test/ui/regions/regions-fn-subtyping-return-static.rs5
-rw-r--r--src/test/ui/regions/regions-fn-subtyping-return-static.stderr30
-rw-r--r--src/test/ui/regions/regions-lifetime-bounds-on-fns.rs2
-rw-r--r--src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr6
-rw-r--r--src/test/ui/where-clauses/where-for-self-2.rs3
-rw-r--r--src/test/ui/where-clauses/where-for-self-2.stderr19
115 files changed, 1563 insertions, 1777 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index e34bb9f4f7b..4cce8343c02 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -132,12 +132,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 
             ty::ReEmpty => ("the empty lifetime".to_owned(), None),
 
+            ty::RePlaceholder(_) => (format!("any other region"), None),
+
             // 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::RePlaceholder(..) | ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
+            ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
                 (format!("lifetime {:?}", region), None)
             }
 
@@ -324,8 +326,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     // 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) => {
-                        self.report_concrete_failure(region_scope_tree, origin, sub, sup)
-                            .emit();
+                        if sub.is_placeholder() || sup.is_placeholder() {
+                            self.report_placeholder_failure(region_scope_tree, origin, sub, sup)
+                                .emit();
+                        } else {
+                            self.report_concrete_failure(region_scope_tree, origin, sub, sup)
+                                .emit();
+                        }
                     }
 
                     RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => {
@@ -339,20 +346,39 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     }
 
                     RegionResolutionError::SubSupConflict(
+                        _,
                         var_origin,
                         sub_origin,
                         sub_r,
                         sup_origin,
                         sup_r,
                     ) => {
-                        self.report_sub_sup_conflict(
-                            region_scope_tree,
-                            var_origin,
-                            sub_origin,
-                            sub_r,
-                            sup_origin,
-                            sup_r,
-                        );
+                        if sub_r.is_placeholder() {
+                            self.report_placeholder_failure(
+                                region_scope_tree,
+                                sub_origin,
+                                sub_r,
+                                sup_r,
+                            )
+                                .emit();
+                        } else if sup_r.is_placeholder() {
+                            self.report_placeholder_failure(
+                                region_scope_tree,
+                                sup_origin,
+                                sub_r,
+                                sup_r,
+                            )
+                                .emit();
+                        } else {
+                            self.report_sub_sup_conflict(
+                                region_scope_tree,
+                                var_origin,
+                                sub_origin,
+                                sub_r,
+                                sup_origin,
+                                sup_r,
+                            );
+                        }
                     }
                 }
             }
@@ -407,7 +433,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         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::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(),
         });
         errors
     }
@@ -1306,6 +1332,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         match (&sup_origin, &sub_origin) {
             (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) => {
+                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);
+                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),
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 ecf59daeaf1..f7ba546fa7f 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/mod.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs
@@ -8,6 +8,7 @@ use util::common::ErrorReported;
 mod different_lifetimes;
 mod find_anon_type;
 mod named_anon_conflict;
+mod placeholder_error;
 mod outlives_closure;
 mod static_impl_trait;
 mod util;
@@ -58,10 +59,11 @@ impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, '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_named_anon_conflict()
+        self.try_report_from_nll()
             .or_else(|| self.try_report_anon_anon_conflict())
             .or_else(|| self.try_report_outlives_closure())
             .or_else(|| self.try_report_static_impl_trait())
@@ -69,8 +71,8 @@ impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> {
 
     pub fn get_regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) {
         match (&self.error, self.regions) {
-            (&Some(ConcreteFailure(ref origin, sub, sup)), None) => (origin.span(), sub, sup),
-            (&Some(SubSupConflict(_, ref origin, sub, _, sup)), None) => (origin.span(), sub, sup),
+            (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"),
diff --git a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs
index 789796d95cb..c4c71037d8b 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs
@@ -36,7 +36,8 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
     ///              ...because it cannot outlive this closure
     /// ```
     pub(super) fn try_report_outlives_closure(&self) -> Option<ErrorReported> {
-        if let Some(SubSupConflict(origin,
+        if let Some(SubSupConflict(_,
+                                   origin,
                                    ref sub_origin,
                                    _,
                                    ref sup_origin,
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
new file mode 100644
index 00000000000..0dda636a9bd
--- /dev/null
+++ b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -0,0 +1,307 @@
+use hir::def_id::DefId;
+use infer::error_reporting::nice_region_error::NiceRegionError;
+use infer::lexical_region_resolve::RegionResolutionError;
+use infer::ValuePairs;
+use infer::{SubregionOrigin, TypeTrace};
+use traits::{ObligationCause, ObligationCauseCode};
+use ty;
+use ty::error::ExpectedFound;
+use ty::subst::Substs;
+use util::common::ErrorReported;
+use util::ppaux::RegionHighlightMode;
+
+impl NiceRegionError<'me, 'gcx, 'tcx> {
+    /// When given a `ConcreteFailure` for a function with arguments containing a named region and
+    /// an anonymous region, emit an descriptive diagnostic error.
+    pub(super) fn try_report_placeholder_conflict(&self) -> Option<ErrorReported> {
+        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(TypeTrace {
+                    cause,
+                    values: ValuePairs::TraitRefs(ExpectedFound { expected, found }),
+                }),
+                sub_placeholder @ ty::RePlaceholder(_),
+                _,
+                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,
+                    Some(sub_placeholder),
+                    Some(sup_placeholder),
+                    expected.def_id,
+                    expected.substs,
+                    found.substs,
+                ))
+            }
+
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                SubregionOrigin::Subtype(TypeTrace {
+                    cause,
+                    values: ValuePairs::TraitRefs(ExpectedFound { expected, found }),
+                }),
+                sub_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,
+                    Some(sub_placeholder),
+                    None,
+                    expected.def_id,
+                    expected.substs,
+                    found.substs,
+                ))
+            }
+
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                SubregionOrigin::Subtype(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(TypeTrace {
+                    cause,
+                    values: ValuePairs::TraitRefs(ExpectedFound { expected, found }),
+                }),
+                sub_region @ ty::RePlaceholder(_),
+                sup_region @ ty::RePlaceholder(_),
+            ))
+                if expected.def_id == found.def_id =>
+            {
+                Some(self.try_report_placeholders_trait(
+                    None,
+                    cause,
+                    Some(*sub_region),
+                    Some(*sup_region),
+                    expected.def_id,
+                    expected.substs,
+                    found.substs,
+                ))
+            }
+
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(TypeTrace {
+                    cause,
+                    values: ValuePairs::TraitRefs(ExpectedFound { expected, found }),
+                }),
+                sub_region @ ty::RePlaceholder(_),
+                sup_region,
+            ))
+                if expected.def_id == found.def_id =>
+            {
+                Some(self.try_report_placeholders_trait(
+                    Some(sup_region),
+                    cause,
+                    Some(*sub_region),
+                    None,
+                    expected.def_id,
+                    expected.substs,
+                    found.substs,
+                ))
+            }
+
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(TypeTrace {
+                    cause,
+                    values: ValuePairs::TraitRefs(ExpectedFound { expected, found }),
+                }),
+                sub_region,
+                sup_region @ ty::RePlaceholder(_),
+            ))
+                if expected.def_id == found.def_id =>
+            {
+                Some(self.try_report_placeholders_trait(
+                    Some(sub_region),
+                    cause,
+                    None,
+                    Some(*sup_region),
+                    expected.def_id,
+                    expected.substs,
+                    found.substs,
+                ))
+            }
+
+            _ => None,
+        }
+    }
+
+    // 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`.
+    fn try_report_placeholders_trait(
+        &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: &'tcx Substs<'tcx>,
+        actual_substs: &'tcx Substs<'tcx>,
+    ) -> ErrorReported {
+        let mut err = self.tcx.sess.struct_span_err(
+            cause.span(&self.tcx),
+            &format!(
+                "implementation of `{}` is not general enough",
+                self.tcx.item_path_str(trait_def_id),
+            ),
+        );
+
+        match cause.code {
+            ObligationCauseCode::ItemObligation(def_id) => {
+                err.note(&format!(
+                    "Due to a where-clause on `{}`,",
+                    self.tcx.item_path_str(def_id),
+                ));
+            }
+            _ => (),
+        }
+
+        let expected_trait_ref = ty::TraitRef {
+            def_id: trait_def_id,
+            substs: expected_substs,
+        };
+        let actual_trait_ref = 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 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;
+            }
+        });
+
+        self.tcx.for_each_free_region(&actual_trait_ref, |r| {
+            if Some(r) == vid && has_vid.is_none() {
+                has_vid = Some(counter);
+                counter += 1;
+            }
+        });
+
+        let self_ty_has_vid = self
+            .tcx
+            .any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
+
+        RegionHighlightMode::maybe_highlighting_region(sub_placeholder, has_sub, || {
+            RegionHighlightMode::maybe_highlighting_region(sup_placeholder, has_sup, || {
+                match (has_sub, has_sup) {
+                    (Some(n1), Some(n2)) => {
+                        err.note(&format!(
+                            "`{}` must implement `{}` \
+                             for any two lifetimes `'{}` and `'{}`",
+                            expected_trait_ref.self_ty(),
+                            expected_trait_ref,
+                            std::cmp::min(n1, n2),
+                            std::cmp::max(n1, n2),
+                        ));
+                    }
+                    (Some(n), _) | (_, Some(n)) => {
+                        err.note(&format!(
+                            "`{}` must implement `{}` \
+                             for any lifetime `'{}`",
+                            expected_trait_ref.self_ty(),
+                            expected_trait_ref,
+                            n,
+                        ));
+                    }
+                    (None, None) => {
+                        err.note(&format!(
+                            "`{}` must implement `{}`",
+                            expected_trait_ref.self_ty(),
+                            expected_trait_ref,
+                        ));
+                    }
+                }
+            })
+        });
+
+        RegionHighlightMode::maybe_highlighting_region(vid, has_vid, || match has_vid {
+            Some(n) => {
+                if self_ty_has_vid {
+                    err.note(&format!(
+                        "but `{}` only implements `{}` for the lifetime `'{}`",
+                        actual_trait_ref.self_ty(),
+                        actual_trait_ref,
+                        n
+                    ));
+                } else {
+                    err.note(&format!(
+                        "but `{}` only implements `{}` for some lifetime `'{}`",
+                        actual_trait_ref.self_ty(),
+                        actual_trait_ref,
+                        n
+                    ));
+                }
+            }
+            None => {
+                err.note(&format!(
+                    "but `{}` only implements `{}`",
+                    actual_trait_ref.self_ty(),
+                    actual_trait_ref,
+                ));
+            }
+        });
+
+        err.emit();
+        ErrorReported
+    }
+}
diff --git a/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs
index 6d2d2ee93ab..9fc3bb05cda 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -11,6 +11,7 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
     pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
         if let Some(ref error) = self.error {
             if let RegionResolutionError::SubSupConflict(
+                    _,
                     var_origin,
                     sub_origin,
                     sub_r,
diff --git a/src/librustc/infer/error_reporting/note.rs b/src/librustc/infer/error_reporting/note.rs
index 91da3197d3c..e45a4b17cdd 100644
--- a/src/librustc/infer/error_reporting/note.rs
+++ b/src/librustc/infer/error_reporting/note.rs
@@ -442,4 +442,24 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             }
         }
     }
+
+    pub(super) fn report_placeholder_failure(
+        &self,
+        region_scope_tree: &region::ScopeTree,
+        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
+        match placeholder_origin {
+            infer::Subtype(trace) => {
+                let terr = TypeError::RegionsPlaceholderMismatch;
+                self.report_and_explain_type_error(trace, &terr)
+            }
+
+            _ => {
+                self.report_concrete_failure(region_scope_tree, placeholder_origin, sub, sup)
+            }
+        }
+    }
 }
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index 3a3a21d0f1d..709e8c0ba9b 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -1,31 +1,23 @@
 //! Helper routines for higher-ranked things. See the `doc` module at
 //! the end of the file for details.
 
-use super::{CombinedSnapshot,
-            InferCtxt,
-            HigherRankedType,
-            SubregionOrigin,
-            PlaceholderMap};
 use super::combine::CombineFields;
-use super::region_constraints::{TaintDirections};
+use super::{HigherRankedType, InferCtxt, PlaceholderMap};
 
-use ty::{self, TyCtxt, Binder, TypeFoldable};
-use ty::error::TypeError;
 use ty::relate::{Relate, RelateResult, TypeRelation};
-use syntax_pos::Span;
-use util::nodemap::{FxHashMap, FxHashSet};
-
-pub struct HrMatchResult<U> {
-    pub value: U,
-}
+use ty::{self, Binder, TypeFoldable};
 
 impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
-    pub fn higher_ranked_sub<T>(&mut self, a: &Binder<T>, b: &Binder<T>, a_is_expected: bool)
-                                -> RelateResult<'tcx, Binder<T>>
-        where T: Relate<'tcx>
+    pub fn higher_ranked_sub<T>(
+        &mut self,
+        a: &Binder<T>,
+        b: &Binder<T>,
+        a_is_expected: bool,
+    ) -> RelateResult<'tcx, Binder<T>>
+    where
+        T: Relate<'tcx>,
     {
-        debug!("higher_ranked_sub(a={:?}, b={:?})",
-               a, b);
+        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
@@ -35,279 +27,37 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         // please see the large comment at the end of the file in the (inlined) module
         // `doc`.
 
-        // Start a snapshot so we can examine "all bindings that were
-        // created as part of this type comparison".
-        return self.infcx.commit_if_ok(|snapshot| {
-            let span = self.trace.cause.span;
-
-            // First, we instantiate each bound region in the supertype with a
-            // fresh placeholder region.
-            let (b_prime, placeholder_map) =
-                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)?;
-
-            // Presuming type comparison succeeds, we need to check
-            // that the placeholder regions do not "leak".
-            self.infcx.leak_check(!a_is_expected, span, &placeholder_map, snapshot)?;
-
-            // We are finished with the placeholder regions now so pop
-            // them off.
-            self.infcx.pop_placeholders(placeholder_map, snapshot);
-
-            debug!("higher_ranked_sub: OK result={:?}", result);
-
-            Ok(ty::Binder::bind(result))
-        });
-    }
-
-    /// The value consists of a pair `(t, u)` where `t` is the
-    /// *matcher* and `u` is a *value*. The idea is to find a
-    /// substitution `S` such that `S(t) == b`, and then return
-    /// `S(u)`. In other words, find values for the late-bound regions
-    /// in `a` that can make `t == b` and then replace the LBR in `u`
-    /// with those values.
-    ///
-    /// This routine is (as of this writing) used in trait matching,
-    /// particularly projection.
-    ///
-    /// NB. It should not happen that there are LBR appearing in `U`
-    /// that do not appear in `T`. If that happens, those regions are
-    /// unconstrained, and this routine replaces them with `'static`.
-    pub fn higher_ranked_match<T, U>(&mut self,
-                                     a_pair: &Binder<(T, U)>,
-                                     b_match: &T,
-                                     a_is_expected: bool)
-                                     -> RelateResult<'tcx, HrMatchResult<U>>
-        where T: Relate<'tcx>,
-              U: TypeFoldable<'tcx>
-    {
-        debug!("higher_ranked_match(a={:?}, b={:?})",
-               a_pair, b_match);
-
-        // Start a snapshot so we can examine "all bindings that were
-        // created as part of this type comparison".
-        return self.infcx.commit_if_ok(|snapshot| {
-            // First, we instantiate each bound region in the matcher
-            // with a placeholder region.
-            let ((a_match, a_value), placeholder_map) =
-                self.infcx.replace_bound_vars_with_placeholders(a_pair);
+        let span = self.trace.cause.span;
 
-            debug!("higher_ranked_match: a_match={:?}", a_match);
-            debug!("higher_ranked_match: placeholder_map={:?}", placeholder_map);
+        // 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);
 
-            // Equate types now that bound regions have been replaced.
-            self.equate(a_is_expected).relate(&a_match, &b_match)?;
+        // 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);
 
-            // Map each placeholder region to a vector of other regions that it
-            // must be equated with. (Note that this vector may include other
-            // placeholder regions from `placeholder_map`.)
-            let placeholder_resolution_map: FxHashMap<_, _> =
-                placeholder_map
-                .iter()
-                .map(|(&br, &placeholder)| {
-                    let tainted_regions =
-                        self.infcx.tainted_regions(snapshot,
-                                                   placeholder,
-                                                   TaintDirections::incoming()); // [1]
+        debug!("a_prime={:?}", a_prime);
+        debug!("b_prime={:?}", b_prime);
 
-                    // [1] this routine executes after the placeholder
-                    // regions have been *equated* with something
-                    // else, so examining the incoming edges ought to
-                    // be enough to collect all constraints
+        // Compare types now that bound regions have been replaced.
+        let result = self.sub(a_is_expected).relate(&a_prime, &b_prime)?;
 
-                    (placeholder, (br, tainted_regions))
-                })
-                .collect();
+        debug!("higher_ranked_sub: OK result={:?}", result);
 
-            // For each placeholder region, pick a representative -- which can
-            // be any region from the sets above, except for other members of
-            // `placeholder_map`. There should always be a representative if things
-            // are properly well-formed.
-            let placeholder_representatives: FxHashMap<_, _> =
-                placeholder_resolution_map
-                .iter()
-                .map(|(&placeholder, &(_, ref regions))| {
-                    let representative =
-                        regions.iter()
-                               .filter(|&&r| !placeholder_resolution_map.contains_key(r))
-                               .cloned()
-                               .next()
-                               .unwrap_or_else(|| {
-                                   bug!("no representative region for `{:?}` in `{:?}`",
-                                        placeholder, regions)
-                               });
-
-                    (placeholder, representative)
-                })
-                .collect();
-
-            // Equate all the members of each placeholder set with the
-            // representative.
-            for (placeholder, &(_br, ref regions)) in &placeholder_resolution_map {
-                let representative = &placeholder_representatives[placeholder];
-                debug!("higher_ranked_match: \
-                        placeholder={:?} representative={:?} regions={:?}",
-                       placeholder, representative, regions);
-                for region in regions.iter()
-                                     .filter(|&r| !placeholder_resolution_map.contains_key(r))
-                                     .filter(|&r| r != representative)
-                {
-                    let origin = SubregionOrigin::Subtype(self.trace.clone());
-                    self.infcx.borrow_region_constraints()
-                              .make_eqregion(origin,
-                                             *representative,
-                                             *region);
-                }
-            }
-
-            // Replace the placeholder regions appearing in value with
-            // their representatives
-            let a_value =
-                fold_regions_in(
-                    self.tcx(),
-                    &a_value,
-                    |r, _| placeholder_representatives.get(&r).cloned().unwrap_or(r));
-
-            debug!("higher_ranked_match: value={:?}", a_value);
-
-            // We are now done with these placeholder variables.
-            self.infcx.pop_placeholders(placeholder_map, snapshot);
-
-            Ok(HrMatchResult { value: a_value })
-        });
+        Ok(ty::Binder::bind(result))
     }
 }
 
-fn fold_regions_in<'a, 'gcx, 'tcx, T, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                                         unbound_value: &T,
-                                         mut fldr: F)
-                                         -> T
-    where T: TypeFoldable<'tcx>,
-          F: FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx>,
-{
-    tcx.fold_regions(unbound_value, &mut false, |region, current_depth| {
-        // we should only be encountering "escaping" late-bound regions here,
-        // because the ones at the current level should have been replaced
-        // with fresh variables
-        assert!(match *region {
-            ty::ReLateBound(..) => false,
-            _ => true
-        });
-
-        fldr(region, current_depth)
-    })
-}
-
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
-    fn tainted_regions(&self,
-                       snapshot: &CombinedSnapshot<'a, 'tcx>,
-                       r: ty::Region<'tcx>,
-                       directions: TaintDirections)
-                       -> FxHashSet<ty::Region<'tcx>> {
-        self.borrow_region_constraints().tainted(
-            self.tcx,
-            &snapshot.region_constraints_snapshot,
-            r,
-            directions)
-    }
-
-    fn region_vars_confined_to_snapshot(&self,
-                                        snapshot: &CombinedSnapshot<'a, 'tcx>)
-                                        -> Vec<ty::RegionVid>
-    {
-        /*!
-         * Returns the set of region variables that do not affect any
-         * types/regions which existed before `snapshot` was
-         * started. This is used in the sub/lub/glb computations. The
-         * idea here is that when we are computing lub/glb of two
-         * regions, we sometimes create intermediate region variables.
-         * Those region variables may touch some of the placeholder or
-         * other "forbidden" regions we created to replace bound
-         * regions, but they don't really represent an "external"
-         * constraint.
-         *
-         * However, sometimes fresh variables are created for other
-         * purposes too, and those *may* represent an external
-         * constraint. In particular, when a type variable is
-         * instantiated, we create region variables for all the
-         * regions that appear within, and if that type variable
-         * pre-existed the snapshot, then those region variables
-         * represent external constraints.
-         *
-         * An example appears in the unit test
-         * `sub_free_bound_false_infer`.  In this test, we want to
-         * know whether
-         *
-         * ```rust
-         * fn(_#0t) <: for<'a> fn(&'a int)
-         * ```
-         *
-         * Note that the subtype has a type variable. Because the type
-         * variable can't be instantiated with a region that is bound
-         * in the fn signature, this comparison ought to fail. But if
-         * we're not careful, it will succeed.
-         *
-         * The reason is that when we walk through the subtyping
-         * algorithm, we begin by replacing `'a` with a placeholder
-         * variable `'1`. We then have `fn(_#0t) <: fn(&'1 int)`. This
-         * can be made true by unifying `_#0t` with `&'1 int`. In the
-         * process, we create a fresh variable for the placeholder
-         * region, `'$2`, and hence we have that `_#0t == &'$2
-         * int`. However, because `'$2` was created during the sub
-         * computation, if we're not careful we will erroneously
-         * assume it is one of the transient region variables
-         * representing a lub/glb internally. Not good.
-         *
-         * To prevent this, we check for type variables which were
-         * unified during the snapshot, and say that any region
-         * variable created during the snapshot but which finds its
-         * way into a type variable is considered to "escape" the
-         * snapshot.
-         */
-
-        let mut region_vars =
-            self.borrow_region_constraints().vars_created_since_snapshot(
-                &snapshot.region_constraints_snapshot);
-
-        let escaping_types =
-            self.type_variables.borrow_mut().types_escaping_snapshot(&snapshot.type_snapshot);
-
-        let mut escaping_region_vars = FxHashSet::default();
-        for ty in &escaping_types {
-            self.tcx.collect_regions(ty, &mut escaping_region_vars);
-        }
-
-        region_vars.retain(|&region_vid| {
-            let r = ty::ReVar(region_vid);
-            !escaping_region_vars.contains(&r)
-        });
-
-        debug!("region_vars_confined_to_snapshot: region_vars={:?} escaping_types={:?}",
-               region_vars,
-               escaping_types);
-
-        region_vars
-    }
-
     /// Replace all regions (resp. types) bound by `binder` with placeholder
     /// regions (resp. types) and return a map indicating which bound-region
-    /// was replaced with what placeholder region. This is the first step of
-    /// checking subtyping when higher-ranked things are involved.
+    /// placeholder region. This is the first step of checking subtyping
+    /// when higher-ranked things are involved.
     ///
     /// **Important:** you must call this function from within a snapshot.
     /// Moreover, before committing the snapshot, you must eventually call
@@ -354,201 +104,4 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         (result, map)
     }
-
-    /// Searches the region constraints created since `snapshot` was started
-    /// and checks to determine whether any of the placeholder regions created
-    /// in `placeholder_map` would "escape" -- meaning that they are related to
-    /// other regions in some way. If so, the higher-ranked subtyping doesn't
-    /// hold. See `README.md` for more details.
-    pub fn leak_check(&self,
-                      overly_polymorphic: bool,
-                      _span: Span,
-                      placeholder_map: &PlaceholderMap<'tcx>,
-                      snapshot: &CombinedSnapshot<'a, 'tcx>)
-                      -> RelateResult<'tcx, ()>
-    {
-        debug!("leak_check: placeholder_map={:?}",
-               placeholder_map);
-
-        // 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 self.tcx.sess.opts.debugging_opts.no_leak_check {
-            return Ok(());
-        }
-
-        let new_vars = self.region_vars_confined_to_snapshot(snapshot);
-        for (&placeholder_br, &placeholder) in placeholder_map {
-            // The inputs to a placeholder variable can only
-            // be itself or other new variables.
-            let incoming_taints = self.tainted_regions(snapshot,
-                                                       placeholder,
-                                                       TaintDirections::both());
-            for &tainted_region in &incoming_taints {
-                // Each placeholder should only be relatable to itself
-                // or new variables:
-                match *tainted_region {
-                    ty::ReVar(vid) => {
-                        if new_vars.contains(&vid) {
-                            continue;
-                        }
-                    }
-                    _ => {
-                        if tainted_region == placeholder { continue; }
-                    }
-                };
-
-                debug!("{:?} (which replaced {:?}) is tainted by {:?}",
-                       placeholder,
-                       placeholder_br,
-                       tainted_region);
-
-                return Err(if overly_polymorphic {
-                    debug!("Overly polymorphic!");
-                    TypeError::RegionsOverlyPolymorphic(placeholder_br, tainted_region)
-                } else {
-                    debug!("Not as polymorphic!");
-                    TypeError::RegionsInsufficientlyPolymorphic(placeholder_br, tainted_region)
-                })
-            }
-        }
-
-        Ok(())
-    }
-
-    /// This code converts from placeholder regions back to late-bound
-    /// regions. It works by replacing each region in the taint set of a
-    /// placeholder region with a bound-region. The bound region will be bound
-    /// by the outer-most binder in `value`; the caller must ensure that there is
-    /// such a binder and it is the right place.
-    ///
-    /// This routine is only intended to be used when the leak-check has
-    /// passed; currently, it's used in the trait matching code to create
-    /// a set of nested obligations from an impl that matches against
-    /// something higher-ranked.  More details can be found in
-    /// `librustc/middle/traits/README.md`.
-    ///
-    /// As a brief example, consider the obligation `for<'a> Fn(&'a int)
-    /// -> &'a int`, and the impl:
-    ///
-    ///     impl<A,R> Fn<A,R> for SomethingOrOther
-    ///         where A : Clone
-    ///     { ... }
-    ///
-    /// Here we will have replaced `'a` with a placeholder region
-    /// `'0`. This means that our substitution will be `{A=>&'0
-    /// int, R=>&'0 int}`.
-    ///
-    /// When we apply the substitution to the bounds, we will wind up with
-    /// `&'0 int : Clone` as a predicate. As a last step, we then go and
-    /// replace `'0` with a late-bound region `'a`.  The depth is matched
-    /// to the depth of the predicate, in this case 1, so that the final
-    /// predicate is `for<'a> &'a int : Clone`.
-    pub fn plug_leaks<T>(&self,
-                         placeholder_map: PlaceholderMap<'tcx>,
-                         snapshot: &CombinedSnapshot<'a, 'tcx>,
-                         value: T) -> T
-        where T : TypeFoldable<'tcx>
-    {
-        debug!("plug_leaks(placeholder_map={:?}, value={:?})",
-               placeholder_map,
-               value);
-
-        if placeholder_map.is_empty() {
-            return value;
-        }
-
-        // Compute a mapping from the "taint set" of each placeholder
-        // region back to the `ty::BoundRegion` that it originally
-        // represented. Because `leak_check` passed, we know that
-        // these taint sets are mutually disjoint.
-        let inv_placeholder_map: FxHashMap<ty::Region<'tcx>, ty::BoundRegion> =
-            placeholder_map
-            .iter()
-            .flat_map(|(&placeholder_br, &placeholder)| {
-                self.tainted_regions(snapshot, placeholder, TaintDirections::both())
-                    .into_iter()
-                    .map(move |tainted_region| (tainted_region, placeholder_br))
-            })
-            .collect();
-
-        debug!("plug_leaks: inv_placeholder_map={:?}",
-               inv_placeholder_map);
-
-        // Remove any instantiated type variables from `value`; those can hide
-        // references to regions from the `fold_regions` code below.
-        let value = self.resolve_type_vars_if_possible(&value);
-
-        // Map any placeholder byproducts back to a late-bound
-        // region. Put that late-bound region at whatever the outermost
-        // binder is that we encountered in `value`. The caller is
-        // responsible for ensuring that (a) `value` contains at least one
-        // binder and (b) that binder is the one we want to use.
-        let result = self.tcx.fold_regions(&value, &mut false, |r, current_depth| {
-            match inv_placeholder_map.get(&r) {
-                None => r,
-                Some(br) => {
-                    // It is the responsibility of the caller to ensure
-                    // that each placeholder region appears within a
-                    // binder. In practice, this routine is only used by
-                    // trait checking, and all of the placeholder regions
-                    // appear inside predicates, which always have
-                    // binders, so this assert is satisfied.
-                    assert!(current_depth > ty::INNERMOST);
-
-                    // since leak-check passed, this placeholder region
-                    // should only have incoming edges from variables
-                    // (which ought not to escape the snapshot, but we
-                    // don't check that) or itself
-                    assert!(
-                        match *r {
-                            ty::ReVar(_) => true,
-                            ty::RePlaceholder(index) => index.name == *br,
-                            _ => false,
-                        },
-                        "leak-check would have us replace {:?} with {:?}",
-                        r, br);
-
-                    self.tcx.mk_region(ty::ReLateBound(
-                        current_depth.shifted_out(1),
-                        br.clone(),
-                    ))
-                }
-            }
-        });
-
-        self.pop_placeholders(placeholder_map, snapshot);
-
-        debug!("plug_leaks: result={:?}", result);
-
-        result
-    }
-
-    /// Pops the placeholder regions found in `placeholder_map` from the region
-    /// inference context. Whenever you create placeholder regions via
-    /// `replace_bound_vars_with_placeholders`, they must be popped before you
-    /// commit the enclosing snapshot (if you do not commit, e.g., within a
-    /// probe or as a result of an error, then this is not necessary, as
-    /// popping happens as part of the rollback).
-    ///
-    /// Note: popping also occurs implicitly as part of `leak_check`.
-    pub fn pop_placeholders(
-        &self,
-        placeholder_map: PlaceholderMap<'tcx>,
-        snapshot: &CombinedSnapshot<'a, 'tcx>,
-    ) {
-        debug!("pop_placeholders({:?})", placeholder_map);
-        let placeholder_regions: FxHashSet<_> = placeholder_map.values().cloned().collect();
-        self.borrow_region_constraints().pop_placeholders(&placeholder_regions);
-        self.universe.set(snapshot.universe);
-        if !placeholder_map.is_empty() {
-            self.projection_cache.borrow_mut().rollback_placeholder(
-                &snapshot.projection_cache_snapshot);
-        }
-    }
 }
diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs
index de64800ee8d..dbf8f270ab0 100644
--- a/src/librustc/infer/lexical_region_resolve/mod.rs
+++ b/src/librustc/infer/lexical_region_resolve/mod.rs
@@ -73,12 +73,13 @@ pub enum RegionResolutionError<'tcx> {
     /// `a` (but none of the known bounds are sufficient).
     GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>),
 
-    /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`:
+    /// `SubSupConflict(v, v_origin, sub_origin, sub_r, sup_origin, sup_r)`:
     ///
-    /// Could not infer a value for `v` because `sub_r <= v` (due to
-    /// `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and
+    /// 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>,
@@ -215,23 +216,41 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
     ) -> bool {
         debug!("expand_node({:?}, {:?} == {:?})", a_region, b_vid, b_data);
 
-        // Check if this relationship is implied by a given.
         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) => {
-                let lub = self.lub_concrete_regions(a_region, cur_region);
+                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 }`.)
+                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().types.re_static;
+                    }
+                }
+
                 debug!(
                     "Expanding value of {:?} from {:?} to {:?}",
                     b_vid, cur_region, lub
@@ -554,10 +573,22 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
         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().types.re_static
+                } else {
+                    lower_bound.region
+                }
+            } else {
+                lower_bound.region
+            };
+
             for upper_bound in &upper_bounds {
                 if !self.region_rels
-                    .is_subregion_of(lower_bound.region, upper_bound.region)
+                    .is_subregion_of(effective_lower_bound, upper_bound.region)
                 {
                     let origin = self.var_infos[node_idx].origin.clone();
                     debug!(
@@ -566,6 +597,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
                         origin, node_idx, lower_bound.region, upper_bound.region
                     );
                     errors.push(RegionResolutionError::SubSupConflict(
+                        node_idx,
                         origin,
                         lower_bound.origin.clone(),
                         lower_bound.region,
@@ -580,9 +612,10 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
         span_bug!(
             self.var_infos[node_idx].origin.span(),
             "collect_error_for_expanding_node() could not find \
-             error for var {:?}, lower_bounds={:?}, \
-             upper_bounds={:?}",
+             error for var {:?} in universe {:?}, lower_bounds={:#?}, \
+             upper_bounds={:#?}",
             node_idx,
+            node_universe,
             lower_bounds,
             upper_bounds
         );
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index c000e3aa013..2d3fb137faf 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -32,7 +32,6 @@ use ty::{FloatVid, IntVid, TyVid};
 use util::nodemap::FxHashMap;
 
 use self::combine::CombineFields;
-use self::higher_ranked::HrMatchResult;
 use self::lexical_region_resolve::LexicalRegionResolutions;
 use self::outlives::env::OutlivesEnvironment;
 use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, VerifyBound};
@@ -226,7 +225,7 @@ pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
 pub type PlaceholderMap<'tcx> = BTreeMap<ty::BoundRegion, ty::Region<'tcx>>;
 
 /// See `error_reporting` module for more details
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum ValuePairs<'tcx> {
     Types(ExpectedFound<Ty<'tcx>>),
     Regions(ExpectedFound<ty::Region<'tcx>>),
@@ -868,6 +867,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         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 placehodler
+    /// - 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.borrow_region_constraints().region_constraints_added_in_snapshot(
+            &snapshot.region_constraints_snapshot,
+        )
+    }
+
     pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) {
         self.borrow_region_constraints().add_given(sub, sup);
     }
@@ -939,39 +952,32 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             return None;
         }
 
-        Some(self.commit_if_ok(|snapshot| {
-            let (
-                ty::SubtypePredicate {
-                    a_is_expected,
-                    a,
-                    b,
-                },
-                placeholder_map,
-            ) = self.replace_bound_vars_with_placeholders(predicate);
+        let (
+            ty::SubtypePredicate {
+                a_is_expected,
+                a,
+                b,
+            },
+            _,
+        ) = self.replace_bound_vars_with_placeholders(predicate);
 
-            let cause_span = cause.span;
-            let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?;
-            self.leak_check(false, cause_span, &placeholder_map, snapshot)?;
-            self.pop_placeholders(placeholder_map, snapshot);
-            Ok(ok.unit())
-        }))
+        Some(
+            self.at(cause, param_env)
+                .sub_exp(a_is_expected, a, b)
+                .map(|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), placeholder_map) =
-                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`
-            self.leak_check(false, cause.span, &placeholder_map, snapshot)?;
-            Ok(self.pop_placeholders(placeholder_map, 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`
     }
 
     pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
@@ -1380,46 +1386,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.tcx.replace_bound_vars(value, fld_r, fld_t)
     }
 
-    /// Given a higher-ranked projection predicate like:
-    ///
-    ///     for<'a> <T as Fn<&'a u32>>::Output = &'a u32
-    ///
-    /// and a target trait-ref like:
-    ///
-    ///     <T as Fn<&'x u32>>
-    ///
-    /// find a substitution `S` for the higher-ranked regions (here,
-    /// `['a => 'x]`) such that the predicate matches the trait-ref,
-    /// and then return the value (here, `&'a u32`) but with the
-    /// substitution applied (hence, `&'x u32`).
-    ///
-    /// See `higher_ranked_match` in `higher_ranked/mod.rs` for more
-    /// details.
-    pub fn match_poly_projection_predicate(
-        &self,
-        cause: ObligationCause<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        match_a: ty::PolyProjectionPredicate<'tcx>,
-        match_b: ty::TraitRef<'tcx>,
-    ) -> InferResult<'tcx, HrMatchResult<Ty<'tcx>>> {
-        let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref(self.tcx), p.ty));
-        let trace = TypeTrace {
-            cause,
-            values: TraitRefs(ExpectedFound::new(
-                true,
-                match_pair.skip_binder().0,
-                match_b,
-            )),
-        };
-
-        let mut combine = self.combine_fields(trace, param_env);
-        let result = combine.higher_ranked_match(&match_pair, &match_b, true)?;
-        Ok(InferOk {
-            value: result,
-            obligations: combine.obligations,
-        })
-    }
-
     /// See `verify_generic_bound` method in `region_constraints`
     pub fn verify_generic_bound(
         &self,
@@ -1434,18 +1400,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             .verify_generic_bound(origin, kind, a, bound);
     }
 
-    pub fn type_moves_by_default(
+    pub fn type_is_copy_modulo_regions(
         &self,
         param_env: ty::ParamEnv<'tcx>,
         ty: Ty<'tcx>,
         span: Span,
     ) -> bool {
         let ty = self.resolve_type_vars_if_possible(&ty);
+
         // Even if the type may have no inference variables, during
         // type-checking closure types are in local tables only.
         if !self.in_progress_tables.is_some() || !ty.has_closure_types() {
             if let Some((param_env, ty)) = self.tcx.lift_to_global(&(param_env, ty)) {
-                return ty.moves_by_default(self.tcx.global_tcx(), param_env, span);
+                return ty.is_copy_modulo_regions(self.tcx.global_tcx(), param_env, span);
             }
         }
 
@@ -1455,7 +1422,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         // rightly refuses to work with inference variables, but
         // moves_by_default has a cache, which we want to use in other
         // cases.
-        !traits::type_known_to_meet_bound(self, param_env, ty, copy_def_id, span)
+        traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span)
     }
 
     /// Obtains the latest type of the given closure; this may be a
diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs
index 66ebbe111db..56ae850226c 100644
--- a/src/librustc/infer/region_constraints/mod.rs
+++ b/src/librustc/infer/region_constraints/mod.rs
@@ -17,8 +17,6 @@ use ty::{Region, RegionVid};
 use std::collections::BTreeMap;
 use std::{cmp, fmt, mem, u32};
 
-mod taint;
-
 #[derive(Default)]
 pub struct RegionConstraintCollector<'tcx> {
     /// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
@@ -130,6 +128,16 @@ pub enum Constraint<'tcx> {
     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(),
+        }
+    }
+}
+
 /// VerifyGenericBound(T, _, R, RS): The parameter type `T` (or
 /// associated type) must outlive the region `R`. `T` is known to
 /// outlive `RS`. Therefore verify that `R <= RS[i]` for some
@@ -326,6 +334,8 @@ impl TaintDirections {
     }
 }
 
+pub struct ConstraintInfo {}
+
 impl<'tcx> RegionConstraintCollector<'tcx> {
     pub fn new() -> Self {
         Self::default()
@@ -487,7 +497,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
     ) -> RegionVid {
         let vid = self.var_infos.push(RegionVariableInfo { origin, universe });
 
-        let u_vid = self.unification_table
+        let u_vid = self
+            .unification_table
             .new_key(unify_key::RegionVidKey { min_vid: vid });
         assert_eq!(vid, u_vid);
         if self.in_snapshot() {
@@ -519,7 +530,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
 
         assert!(self.in_snapshot());
 
-        let constraints_to_kill: Vec<usize> = self.undo_log
+        let constraints_to_kill: Vec<usize> = self
+            .undo_log
             .iter()
             .enumerate()
             .rev()
@@ -822,37 +834,18 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
             .filter_map(|&elt| match elt {
                 AddVar(vid) => Some(vid),
                 _ => None,
-            })
-            .collect()
-    }
-
-    /// Computes all regions that have been related to `r0` since the
-    /// mark `mark` was made---`r0` itself will be the first
-    /// entry. The `directions` parameter controls what kind of
-    /// relations are considered. For example, one can say that only
-    /// "incoming" edges to `r0` are desired, in which case one will
-    /// get the set of regions `{r|r <= r0}`. This is used when
-    /// checking whether placeholder regions are being improperly
-    /// related to other regions.
-    pub fn tainted(
-        &self,
-        tcx: TyCtxt<'_, '_, 'tcx>,
-        mark: &RegionSnapshot,
-        r0: Region<'tcx>,
-        directions: TaintDirections,
-    ) -> FxHashSet<ty::Region<'tcx>> {
-        debug!(
-            "tainted(mark={:?}, r0={:?}, directions={:?})",
-            mark, r0, directions
-        );
+            }).collect()
+    }
 
-        // `result_set` acts as a worklist: we explore all outgoing
-        // edges and add any new regions we find to result_set.  This
-        // is not a terribly efficient implementation.
-        let mut taint_set = taint::TaintSet::new(directions, r0);
-        taint_set.fixed_point(tcx, &self.undo_log[mark.length..], &self.data.verifys);
-        debug!("tainted: result={:?}", taint_set);
-        return taint_set.into_set();
+    /// See [`RegionInference::region_constraints_added_in_snapshot`]
+    pub fn region_constraints_added_in_snapshot(&self, mark: &RegionSnapshot) -> Option<bool> {
+        self.undo_log[mark.length..]
+            .iter()
+            .map(|&elt| match elt {
+                AddConstraint(constraint) => Some(constraint.involves_placeholders()),
+                _ => None,
+            }).max()
+            .unwrap_or(None)
     }
 }
 
diff --git a/src/librustc/infer/region_constraints/taint.rs b/src/librustc/infer/region_constraints/taint.rs
deleted file mode 100644
index 6743f37972a..00000000000
--- a/src/librustc/infer/region_constraints/taint.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use super::*;
-
-#[derive(Debug)]
-pub(super) struct TaintSet<'tcx> {
-    directions: TaintDirections,
-    regions: FxHashSet<ty::Region<'tcx>>,
-}
-
-impl<'tcx> TaintSet<'tcx> {
-    pub(super) fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self {
-        let mut regions = FxHashSet::default();
-        regions.insert(initial_region);
-        TaintSet {
-            directions: directions,
-            regions: regions,
-        }
-    }
-
-    pub(super) fn fixed_point(
-        &mut self,
-        tcx: TyCtxt<'_, '_, 'tcx>,
-        undo_log: &[UndoLog<'tcx>],
-        verifys: &[Verify<'tcx>],
-    ) {
-        let mut prev_len = 0;
-        while prev_len < self.len() {
-            debug!(
-                "tainted: prev_len = {:?} new_len = {:?}",
-                prev_len,
-                self.len()
-            );
-
-            prev_len = self.len();
-
-            for undo_entry in undo_log {
-                match undo_entry {
-                    &AddConstraint(Constraint::VarSubVar(a, b)) => {
-                        self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b)));
-                    }
-                    &AddConstraint(Constraint::RegSubVar(a, b)) => {
-                        self.add_edge(a, tcx.mk_region(ReVar(b)));
-                    }
-                    &AddConstraint(Constraint::VarSubReg(a, b)) => {
-                        self.add_edge(tcx.mk_region(ReVar(a)), b);
-                    }
-                    &AddConstraint(Constraint::RegSubReg(a, b)) => {
-                        self.add_edge(a, b);
-                    }
-                    &AddGiven(a, b) => {
-                        self.add_edge(a, tcx.mk_region(ReVar(b)));
-                    }
-                    &AddVerify(i) => {
-                        span_bug!(
-                            verifys[i].origin.span(),
-                            "we never add verifications while doing higher-ranked things",
-                        )
-                    }
-                    &Purged | &AddCombination(..) | &AddVar(..) => {}
-                }
-            }
-        }
-    }
-
-    pub(super) fn into_set(self) -> FxHashSet<ty::Region<'tcx>> {
-        self.regions
-    }
-
-    fn len(&self) -> usize {
-        self.regions.len()
-    }
-
-    fn add_edge(&mut self, source: ty::Region<'tcx>, target: ty::Region<'tcx>) {
-        if self.directions.incoming {
-            if self.regions.contains(&target) {
-                self.regions.insert(source);
-            }
-        }
-
-        if self.directions.outgoing {
-            if self.regions.contains(&source) {
-                self.regions.insert(target);
-            }
-        }
-    }
-}
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index f9bcbb32229..c1aa25b6b75 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -976,7 +976,7 @@ fn copy_or_move<'a, 'gcx, 'tcx>(mc: &mc::MemCategorizationContext<'a, 'gcx, 'tcx
                                 move_reason: MoveReason)
                                 -> ConsumeMode
 {
-    if mc.type_moves_by_default(param_env, cmt.ty, cmt.span) {
+    if !mc.type_is_copy_modulo_regions(param_env, cmt.ty, cmt.span) {
         Move(move_reason)
     } else {
         Copy
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 207382d5e1f..370f0d1a6c6 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -443,15 +443,16 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
         }
     }
 
-    pub fn type_moves_by_default(&self,
-                                 param_env: ty::ParamEnv<'tcx>,
-                                 ty: Ty<'tcx>,
-                                 span: Span)
-                                 -> bool {
-        self.infcx.map(|infcx| infcx.type_moves_by_default(param_env, ty, span))
+    pub fn type_is_copy_modulo_regions(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+        span: Span,
+    ) -> bool {
+        self.infcx.map(|infcx| infcx.type_is_copy_modulo_regions(param_env, ty, span))
             .or_else(|| {
                 self.tcx.lift_to_global(&(param_env, ty)).map(|(param_env, ty)| {
-                    ty.moves_by_default(self.tcx.global_tcx(), param_env, span)
+                    ty.is_copy_modulo_regions(self.tcx.global_tcx(), param_env, span)
                 })
             })
             .unwrap_or(true)
diff --git a/src/librustc/traits/auto_trait.rs b/src/librustc/traits/auto_trait.rs
index f96c4e9014b..92004ece26d 100644
--- a/src/librustc/traits/auto_trait.rs
+++ b/src/librustc/traits/auto_trait.rs
@@ -771,13 +771,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                     }
                 }
                 &ty::Predicate::RegionOutlives(ref binder) => {
-                    if select
-                        .infcx()
-                        .region_outlives_predicate(&dummy_cause, binder)
-                        .is_err()
-                    {
-                        return false;
-                    }
+                    let () = select.infcx().region_outlives_predicate(&dummy_cause, binder);
                 }
                 &ty::Predicate::TypeOutlives(ref binder) => {
                     match (
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs
index 980c98d2a2d..b3d732ebcd7 100644
--- a/src/librustc/traits/coherence.rs
+++ b/src/librustc/traits/coherence.rs
@@ -4,6 +4,7 @@
 //! [trait-resolution]: https://rust-lang.github.io/rustc-guide/traits/resolution.html
 //! [trait-specialization]: https://rust-lang.github.io/rustc-guide/traits/specialization.html
 
+use infer::CombinedSnapshot;
 use hir::def_id::{DefId, LOCAL_CRATE};
 use syntax_pos::DUMMY_SP;
 use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause};
@@ -33,6 +34,17 @@ pub enum Conflict {
 pub struct OverlapResult<'tcx> {
     pub impl_header: ty::ImplHeader<'tcx>,
     pub intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
+
+    /// True if the overlap might've been permitted before the shift
+    /// to universes.
+    pub involves_placeholder: bool,
+}
+
+pub fn add_placeholder_note(err: &mut ::errors::DiagnosticBuilder<'_>) {
+    err.note(&format!(
+        "this behavior recently changed as a result of a bug fix; \
+         see rust-lang/rust#56105 for details"
+    ));
 }
 
 /// If there are types that satisfy both impls, invokes `on_overlap`
@@ -104,13 +116,22 @@ fn with_fresh_ty_vars<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, '
 
 /// Can both impl `a` and impl `b` be satisfied by a common type (including
 /// `where` clauses)? If so, returns an `ImplHeader` that unifies the two impls.
-fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
-                            a_def_id: DefId,
-                            b_def_id: DefId)
-                            -> Option<OverlapResult<'tcx>>
-{
+fn overlap<'cx, 'gcx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
+    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))
+}
+
+fn overlap_within_probe(
+    selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
+    a_def_id: DefId,
+    b_def_id: DefId,
+    snapshot: &CombinedSnapshot<'_, 'tcx>,
+) -> Option<OverlapResult<'tcx>> {
     // For the purposes of this check, we don't bring any placeholder
     // types into scope; instead, we replace the generic types with
     // fresh type variables, and hence we do our evaluations in an
@@ -158,7 +179,13 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     let impl_header = selcx.infcx().resolve_type_vars_if_possible(&a_impl_header);
     let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
     debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
-    Some(OverlapResult { impl_header, intercrate_ambiguity_causes })
+
+    let involves_placeholder = match selcx.infcx().region_constraints_added_in_snapshot(snapshot) {
+        Some(true) => true,
+        _ => false,
+    };
+
+    Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
 }
 
 pub fn trait_ref_is_knowable<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 0e63ef666c7..21352ac1053 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -728,12 +728,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     }
 
                     ty::Predicate::RegionOutlives(ref predicate) => {
-                        let predicate = self.resolve_type_vars_if_possible(predicate);
-                        let err = self.region_outlives_predicate(&obligation.cause,
-                                                                 &predicate).err().unwrap();
-                        struct_span_err!(self.tcx.sess, span, E0279,
-                            "the requirement `{}` is not satisfied (`{}`)",
-                            predicate, err)
+                        // These errors should show up as region
+                        // inference failures.
+                        panic!("region outlives {:?} failed", predicate);
                     }
 
                     ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => {
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index 556b97dc9bc..2e00d4d4b7c 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -282,7 +282,7 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
                 if data.is_global() {
                     // no type variables present, can use evaluation for better caching.
                     // FIXME: consider caching errors too.
-                    if self.selcx.infcx().predicate_must_hold(&obligation) {
+                    if self.selcx.infcx().predicate_must_hold_considering_regions(&obligation) {
                         debug!("selecting trait `{:?}` at depth {} evaluated to holds",
                                data, obligation.recursion_depth);
                         return ProcessResult::Changed(vec![])
@@ -331,10 +331,8 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
             }
 
             ty::Predicate::RegionOutlives(ref binder) => {
-                match self.selcx.infcx().region_outlives_predicate(&obligation.cause, binder) {
-                    Ok(()) => ProcessResult::Changed(vec![]),
-                    Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)),
-                }
+                let () = self.selcx.infcx().region_outlives_predicate(&obligation.cause, binder);
+                ProcessResult::Changed(vec![])
             }
 
             ty::Predicate::TypeOutlives(ref binder) => {
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 49bd04782b2..b42d742b7f8 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -43,7 +43,8 @@ pub use self::FulfillmentErrorCode::*;
 pub use self::Vtable::*;
 pub use self::ObligationCauseCode::*;
 
-pub use self::coherence::{orphan_check, overlapping_impls, OrphanCheckErr, OverlapResult};
+pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
+pub use self::coherence::{OrphanCheckErr, OverlapResult};
 pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
 pub use self::project::MismatchedProjectionTypes;
 pub use self::project::{normalize, normalize_projection_type, poly_project_and_unify_type};
@@ -628,14 +629,14 @@ pub fn predicates_for_generics<'tcx>(cause: ObligationCause<'tcx>,
 /// `bound` or is not known to meet bound (note that this is
 /// conservative towards *no impl*, which is the opposite of the
 /// `evaluate` methods).
-pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-                                                param_env: ty::ParamEnv<'tcx>,
-                                                ty: Ty<'tcx>,
-                                                def_id: DefId,
-                                                span: Span)
--> bool
-{
-    debug!("type_known_to_meet_bound(ty={:?}, bound={:?})",
+pub fn type_known_to_meet_bound_modulo_regions<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
+    def_id: DefId,
+    span: Span,
+) -> bool {
+    debug!("type_known_to_meet_bound_modulo_regions(ty={:?}, bound={:?})",
            ty,
            infcx.tcx.item_path_str(def_id));
 
@@ -650,7 +651,7 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
         predicate: trait_ref.to_predicate(),
     };
 
-    let result = infcx.predicate_must_hold(&obligation);
+    let result = infcx.predicate_must_hold_modulo_regions(&obligation);
     debug!("type_known_to_meet_ty={:?} bound={} => {:?}",
            ty, infcx.tcx.item_path_str(def_id), result);
 
@@ -677,13 +678,13 @@ pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx
         // assume it is move; linear is always ok.
         match fulfill_cx.select_all_or_error(infcx) {
             Ok(()) => {
-                debug!("type_known_to_meet_bound: ty={:?} bound={} success",
+                debug!("type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success",
                        ty,
                        infcx.tcx.item_path_str(def_id));
                 true
             }
             Err(e) => {
-                debug!("type_known_to_meet_bound: ty={:?} bound={} errors={:?}",
+                debug!("type_known_to_meet_bound_modulo_regions: ty={:?} bound={} errors={:?}",
                        ty,
                        infcx.tcx.item_path_str(def_id),
                        e);
diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs
index 1554afdeefd..31342c250e2 100644
--- a/src/librustc/traits/object_safety.rs
+++ b/src/librustc/traits/object_safety.rs
@@ -568,7 +568,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
 
         self.infer_ctxt().enter(|ref infcx| {
             // the receiver is dispatchable iff the obligation holds
-            infcx.predicate_must_hold(&obligation)
+            infcx.predicate_must_hold_modulo_regions(&obligation)
         })
     }
 
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 952b37b89f2..732ca70dc78 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -13,7 +13,7 @@ use super::{VtableImplData, VtableClosureData, VtableGeneratorData, VtableFnPoin
 use super::util;
 
 use hir::def_id::DefId;
-use infer::{InferCtxt, InferOk};
+use infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
 use infer::type_variable::TypeVariableOrigin;
 use mir::interpret::ConstValue;
 use mir::interpret::{GlobalId};
@@ -192,28 +192,12 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
            obligation);
 
     let infcx = selcx.infcx();
-    infcx.commit_if_ok(|snapshot| {
-        let (placeholder_predicate, placeholder_map) =
+    infcx.commit_if_ok(|_| {
+        let (placeholder_predicate, _) =
             infcx.replace_bound_vars_with_placeholders(&obligation.predicate);
 
-        let skol_obligation = obligation.with(placeholder_predicate);
-        let r = match project_and_unify_type(selcx, &skol_obligation) {
-            Ok(result) => {
-                let span = obligation.cause.span;
-                match infcx.leak_check(false, span, &placeholder_map, snapshot) {
-                    Ok(()) => Ok(infcx.plug_leaks(placeholder_map, snapshot, result)),
-                    Err(e) => {
-                        debug!("poly_project_and_unify_type: leak check encountered error {:?}", e);
-                        Err(MismatchedProjectionTypes { err: e })
-                    }
-                }
-            }
-            Err(e) => {
-                Err(e)
-            }
-        };
-
-        r
+        let placeholder_obligation = obligation.with(placeholder_predicate);
+        project_and_unify_type(selcx, &placeholder_obligation)
     })
 }
 
@@ -1443,17 +1427,25 @@ fn confirm_callable_candidate<'cx, 'gcx, 'tcx>(
 fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    poly_projection: ty::PolyProjectionPredicate<'tcx>)
+    poly_cache_entry: ty::PolyProjectionPredicate<'tcx>)
     -> Progress<'tcx>
 {
     let infcx = selcx.infcx();
-    let cause = obligation.cause.clone();
+    let cause = &obligation.cause;
     let param_env = obligation.param_env;
-    let trait_ref = obligation.predicate.trait_ref(infcx.tcx);
-    match infcx.match_poly_projection_predicate(cause, param_env, poly_projection, trait_ref) {
-        Ok(InferOk { value: ty_match, obligations }) => {
+
+    let (cache_entry, _) =
+        infcx.replace_bound_vars_with_fresh_vars(
+            cause.span,
+            LateBoundRegionConversionTime::HigherRankedType,
+            &poly_cache_entry);
+
+    let cache_trait_ref = cache_entry.projection_ty.trait_ref(infcx.tcx);
+    let obligation_trait_ref = obligation.predicate.trait_ref(infcx.tcx);
+    match infcx.at(cause, param_env).eq(cache_trait_ref, obligation_trait_ref) {
+        Ok(InferOk { value: _, obligations }) => {
             Progress {
-                ty: ty_match.value,
+                ty: cache_entry.ty,
                 obligations,
             }
         }
@@ -1463,7 +1455,7 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
                 "Failed to unify obligation `{:?}` \
                  with poly_projection `{:?}`: {:?}",
                 obligation,
-                poly_projection,
+                poly_cache_entry,
                 e);
         }
     }
diff --git a/src/librustc/traits/query/evaluate_obligation.rs b/src/librustc/traits/query/evaluate_obligation.rs
index 4c1d65c46c5..fdae7d83373 100644
--- a/src/librustc/traits/query/evaluate_obligation.rs
+++ b/src/librustc/traits/query/evaluate_obligation.rs
@@ -16,11 +16,26 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
     /// Evaluates whether the predicate can be satisfied in the given
     /// `ParamEnv`, and returns `false` if not certain. However, this is
     /// not entirely accurate if inference variables are involved.
-    pub fn predicate_must_hold(
+    ///
+    /// This version may conservatively fail when outlives obligations
+    /// are required.
+    pub fn predicate_must_hold_considering_regions(
         &self,
         obligation: &PredicateObligation<'tcx>,
     ) -> bool {
-        self.evaluate_obligation_no_overflow(obligation) == EvaluationResult::EvaluatedToOk
+        self.evaluate_obligation_no_overflow(obligation).must_apply_considering_regions()
+    }
+
+    /// Evaluates whether the predicate can be satisfied in the given
+    /// `ParamEnv`, and returns `false` if not certain. However, this is
+    /// not entirely accurate if inference variables are involved.
+    ///
+    /// This version ignores all outlives constraints.
+    pub fn predicate_must_hold_modulo_regions(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> bool {
+        self.evaluate_obligation_no_overflow(obligation).must_apply_modulo_regions()
     }
 
     /// Evaluate a given predicate, capturing overflow and propagating it back.
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index bd347764cc6..373ec2d5e49 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -29,7 +29,6 @@ use super::{
 
 use dep_graph::{DepKind, DepNodeIndex};
 use hir::def_id::DefId;
-use infer;
 use infer::{InferCtxt, InferOk, TypeFreshener};
 use middle::lang_items;
 use mir::interpret::GlobalId;
@@ -45,7 +44,6 @@ use rustc_target::spec::abi::Abi;
 use std::cmp;
 use std::fmt;
 use std::iter;
-use std::mem;
 use std::rc::Rc;
 use util::nodemap::{FxHashMap, FxHashSet};
 
@@ -328,7 +326,8 @@ enum BuiltinImplConditions<'tcx> {
 /// evaluations.
 ///
 /// The evaluation results are ordered:
-///     - `EvaluatedToOk` implies `EvaluatedToAmbig` implies `EvaluatedToUnknown`
+///     - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions`
+///       implies `EvaluatedToAmbig` implies `EvaluatedToUnknown`
 ///     - `EvaluatedToErr` implies `EvaluatedToRecur`
 ///     - the "union" of evaluation results is equal to their maximum -
 ///     all the "potential success" candidates can potentially succeed,
@@ -337,6 +336,8 @@ enum BuiltinImplConditions<'tcx> {
 pub enum EvaluationResult {
     /// Evaluation successful
     EvaluatedToOk,
+    /// Evaluation successful, but there were unevaluated region obligations
+    EvaluatedToOkModuloRegions,
     /// Evaluation is known to be ambiguous - it *might* hold for some
     /// assignment of inference variables, but it might not.
     ///
@@ -400,9 +401,23 @@ pub enum EvaluationResult {
 }
 
 impl EvaluationResult {
+    /// True if this evaluation result is known to apply, even
+    /// considering outlives constraints.
+    pub fn must_apply_considering_regions(self) -> bool {
+        self == EvaluatedToOk
+    }
+
+    /// True if this evaluation result is known to apply, ignoring
+    /// outlives constraints.
+    pub fn must_apply_modulo_regions(self) -> bool {
+        self <= EvaluatedToOkModuloRegions
+    }
+
     pub fn may_apply(self) -> bool {
         match self {
-            EvaluatedToOk | EvaluatedToAmbig | EvaluatedToUnknown => true,
+            EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToUnknown => {
+                true
+            }
 
             EvaluatedToErr | EvaluatedToRecur => false,
         }
@@ -412,13 +427,14 @@ impl EvaluationResult {
         match self {
             EvaluatedToUnknown | EvaluatedToRecur => true,
 
-            EvaluatedToOk | EvaluatedToAmbig | EvaluatedToErr => false,
+            EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToErr => false,
         }
     }
 }
 
 impl_stable_hash_for!(enum self::EvaluationResult {
     EvaluatedToOk,
+    EvaluatedToOkModuloRegions,
     EvaluatedToAmbig,
     EvaluatedToUnknown,
     EvaluatedToRecur,
@@ -531,33 +547,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         self.infcx
     }
 
-    /// Wraps the inference context's in_snapshot s.t. snapshot handling is only from the selection
-    /// context's self.
-    fn in_snapshot<R, F>(&mut self, f: F) -> R
-    where
-        F: FnOnce(&mut Self, &infer::CombinedSnapshot<'cx, 'tcx>) -> R,
-    {
-        self.infcx.in_snapshot(|snapshot| f(self, snapshot))
-    }
-
-    /// Wraps a probe s.t. obligations collected during it are ignored and old obligations are
-    /// retained.
-    fn probe<R, F>(&mut self, f: F) -> R
-    where
-        F: FnOnce(&mut Self, &infer::CombinedSnapshot<'cx, 'tcx>) -> R,
-    {
-        self.infcx.probe(|snapshot| f(self, snapshot))
-    }
-
-    /// Wraps a commit_if_ok s.t. obligations collected during it are not returned in selection if
-    /// the transaction fails and s.t. old obligations are retained.
-    fn commit_if_ok<T, E, F>(&mut self, f: F) -> Result<T, E>
-    where
-        F: FnOnce(&mut Self, &infer::CombinedSnapshot<'cx, 'tcx>) -> Result<T, E>,
-    {
-        self.infcx.commit_if_ok(|snapshot| f(self, snapshot))
-    }
-
     ///////////////////////////////////////////////////////////////////////////
     // Selection
     //
@@ -639,11 +628,24 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         &mut self,
         obligation: &PredicateObligation<'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
-        self.probe(|this, _| {
+        self.evaluation_probe(|this| {
             this.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
         })
     }
 
+    fn evaluation_probe(
+        &mut self,
+        op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
+            let result = op(self)?;
+            match self.infcx.region_constraints_added_in_snapshot(snapshot) {
+                None => Ok(result),
+                Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)),
+            }
+        })
+    }
+
     /// Evaluates the predicates in `predicates` recursively. Note that
     /// this applies projections in the predicates, and therefore
     /// is run within an inference probe.
@@ -714,92 +716,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 None => Ok(EvaluatedToAmbig),
             },
 
-            ty::Predicate::TypeOutlives(ref binder) => {
-                assert!(!binder.has_escaping_bound_vars());
-                // Check if the type has higher-ranked vars.
-                if binder.skip_binder().0.has_escaping_bound_vars() {
-                    // If so, this obligation is an error (for now). Eventually we should be
-                    // able to support additional cases here, like `for<'a> &'a str: 'a`.
-
-                    // NOTE: this hack is implemented in both trait fulfillment and
-                    // evaluation. If you fix it in one place, make sure you fix it
-                    // in the other.
-
-                    // We don't want to allow this sort of reasoning in intercrate
-                    // mode, for backwards-compatibility reasons.
-                    if self.intercrate.is_some() {
-                        Ok(EvaluatedToAmbig)
-                    } else {
-                        Ok(EvaluatedToErr)
-                    }
-                } else {
-                    // If the type has no late bound vars, then if we assign all
-                    // the inference variables in it to be 'static, then the type
-                    // will be 'static itself.
-                    //
-                    // Therefore, `staticize(T): 'a` holds for any `'a`, so this
-                    // obligation is fulfilled. Because evaluation works with
-                    // staticized types (yes I know this is involved with #21974),
-                    // we are 100% OK here.
-                    Ok(EvaluatedToOk)
-                }
-            }
-
-            ty::Predicate::RegionOutlives(ref binder) => {
-                let ty::OutlivesPredicate(r_a, r_b) = binder.skip_binder();
-
-                if r_a == r_b {
-                    // for<'a> 'a: 'a. OK
-                    Ok(EvaluatedToOk)
-                } else if **r_a == ty::ReStatic {
-                    // 'static: 'x always holds.
-                    //
-                    // This special case is handled somewhat inconsistently - if we
-                    // have an inference variable that is supposed to be equal to
-                    // `'static`, then we don't allow it to be equated to an LBR,
-                    // but if we have a literal `'static`, then we *do*.
-                    //
-                    // This is actually consistent with how our region inference works.
-                    //
-                    // It would appear that this sort of inconsistency would
-                    // cause "instability" problems with evaluation caching. However,
-                    // evaluation caching is only for trait predicates, and when
-                    // trait predicates create nested obligations, they contain
-                    // inference variables for all the regions in the trait - the
-                    // only way this codepath can be reached from trait predicate
-                    // evaluation is when the user typed an explicit `where 'static: 'a`
-                    // lifetime bound (in which case we want to return EvaluatedToOk).
-                    //
-                    // If we ever want to handle inference variables that might be
-                    // equatable with ReStatic, we need to make sure we are not confused by
-                    // technically-allowed-by-RFC-447-but-probably-should-not-be
-                    // impls such as
-                    // ```Rust
-                    // impl<'a, 's, T> X<'s> for T where T: Debug + 'a, 'a: 's
-                    // ```
-                    Ok(EvaluatedToOk)
-                } else if r_a.is_late_bound() || r_b.is_late_bound() {
-                    // There is no current way to prove `for<'a> 'a: 'x`
-                    // unless `'a = 'x`, because there are no bounds involving
-                    // lifetimes.
-
-                    // It might be possible to prove `for<'a> 'x: 'a` by forcing `'x`
-                    // to be `'static`. However, this is not currently done by type
-                    // inference unless `'x` is literally ReStatic. See the comment
-                    // above.
-
-                    // We don't want to allow this sort of reasoning in intercrate
-                    // mode, for backwards-compatibility reasons.
-                    if self.intercrate.is_some() {
-                        Ok(EvaluatedToAmbig)
-                    } else {
-                        Ok(EvaluatedToErr)
-                    }
-                } else {
-                    // Relating 2 inference variable regions. These will
-                    // always hold if our query is "staticized".
-                    Ok(EvaluatedToOk)
-                }
+            ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => {
+                // we do not consider region relationships when
+                // evaluating trait matches
+                Ok(EvaluatedToOkModuloRegions)
             }
 
             ty::Predicate::ObjectSafe(trait_def_id) => {
@@ -1013,6 +933,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         {
             debug!("evaluate_stack({:?}) --> recursive", stack.fresh_trait_ref);
 
+            // Subtle: when checking for a coinductive cycle, we do
+            // not compare using the "freshened trait refs" (which
+            // have erased regions) but rather the fully explicit
+            // trait refs. This is important because it's only a cycle
+            // if the regions match exactly.
             let cycle = stack.iter().skip(1).take(rec_index + 1);
             let cycle = cycle.map(|stack| ty::Predicate::Trait(stack.obligation.predicate));
             if self.coinductive_match(cycle) {
@@ -1075,7 +1000,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             "evaluate_candidate: depth={} candidate={:?}",
             stack.obligation.recursion_depth, candidate
         );
-        let result = self.probe(|this, _| {
+        let result = self.evaluation_probe(|this| {
             let candidate = (*candidate).clone();
             match this.confirm_candidate(stack.obligation, candidate) {
                 Ok(selection) => this.evaluate_predicates_recursively(
@@ -1706,8 +1631,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             _ => return,
         }
 
-        let result = self.probe(|this, snapshot| {
-            this.match_projection_obligation_against_definition_bounds(obligation, snapshot)
+        let result = self.infcx.probe(|_| {
+            self.match_projection_obligation_against_definition_bounds(obligation)
         });
 
         if result {
@@ -1718,16 +1643,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     fn match_projection_obligation_against_definition_bounds(
         &mut self,
         obligation: &TraitObligation<'tcx>,
-        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
     ) -> bool {
         let poly_trait_predicate = self.infcx()
             .resolve_type_vars_if_possible(&obligation.predicate);
-        let (skol_trait_predicate, placeholder_map) = self.infcx()
+        let (skol_trait_predicate, _) = self.infcx()
             .replace_bound_vars_with_placeholders(&poly_trait_predicate);
         debug!(
             "match_projection_obligation_against_definition_bounds: \
-             skol_trait_predicate={:?} placeholder_map={:?}",
-            skol_trait_predicate, placeholder_map
+             skol_trait_predicate={:?}",
+            skol_trait_predicate,
         );
 
         let (def_id, substs) = match skol_trait_predicate.trait_ref.self_ty().sty {
@@ -1759,13 +1683,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         let matching_bound = util::elaborate_predicates(self.tcx(), bounds.predicates)
             .filter_to_traits()
             .find(|bound| {
-                self.probe(|this, _| {
-                    this.match_projection(
+                self.infcx.probe(|_| {
+                    self.match_projection(
                         obligation,
                         bound.clone(),
                         skol_trait_predicate.trait_ref.clone(),
-                        &placeholder_map,
-                        snapshot,
                     )
                 })
             });
@@ -1783,12 +1705,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     obligation,
                     bound,
                     skol_trait_predicate.trait_ref.clone(),
-                    &placeholder_map,
-                    snapshot,
                 );
 
-                self.infcx.pop_placeholders(placeholder_map, snapshot);
-
                 assert!(result);
                 true
             }
@@ -1800,20 +1718,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         obligation: &TraitObligation<'tcx>,
         trait_bound: ty::PolyTraitRef<'tcx>,
         skol_trait_ref: ty::TraitRef<'tcx>,
-        placeholder_map: &infer::PlaceholderMap<'tcx>,
-        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
     ) -> bool {
         debug_assert!(!skol_trait_ref.has_escaping_bound_vars());
-        if self.infcx
+        self.infcx
             .at(&obligation.cause, obligation.param_env)
             .sup(ty::Binder::dummy(skol_trait_ref), trait_bound)
-            .is_err()
-        {
-            return false;
-        }
-
-        self.infcx
-            .leak_check(false, obligation.cause.span, placeholder_map, snapshot)
             .is_ok()
     }
 
@@ -1862,7 +1771,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         stack: &TraitObligationStack<'o, 'tcx>,
         where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
-        self.probe(move |this, _| {
+        self.evaluation_probe(|this| {
             match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
                 Ok(obligations) => {
                     this.evaluate_predicates_recursively(stack.list(), obligations.iter())
@@ -2015,14 +1924,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             obligation.predicate.def_id(),
             obligation.predicate.skip_binder().trait_ref.self_ty(),
             |impl_def_id| {
-                self.probe(|this, snapshot| {
-                    if let Ok(placeholder_map) = this.match_impl(impl_def_id, obligation, snapshot)
+                self.infcx.probe(|_| {
+                    if let Ok(_substs) = self.match_impl(impl_def_id, obligation)
                     {
                         candidates.vec.push(ImplCandidate(impl_def_id));
-
-                        // N.B., we can safely drop the placeholder map
-                        // since we are in a probe.
-                        mem::drop(placeholder_map);
                     }
                 });
             },
@@ -2093,11 +1998,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             obligation.self_ty().skip_binder()
         );
 
-        self.probe(|this, _snapshot| {
+        self.infcx.probe(|_snapshot| {
             // The code below doesn't care about regions, and the
             // self-ty here doesn't escape this probe, so just erase
             // any LBR.
-            let self_ty = this.tcx().erase_late_bound_regions(&obligation.self_ty());
+            let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty());
             let poly_trait_ref = match self_ty.sty {
                 ty::Dynamic(ref data, ..) => {
                     if data.auto_traits()
@@ -2111,7 +2016,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         return;
                     }
 
-                    data.principal().with_self_ty(this.tcx(), self_ty)
+                    data.principal().with_self_ty(self.tcx(), self_ty)
                 }
                 ty::Infer(ty::TyVar(_)) => {
                     debug!("assemble_candidates_from_object_ty: ambiguous");
@@ -2131,11 +2036,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // correct trait, but also the correct type parameters.
             // For example, we may be trying to upcast `Foo` to `Bar<i32>`,
             // but `Foo` is declared as `trait Foo : Bar<u32>`.
-            let upcast_trait_refs = util::supertraits(this.tcx(), poly_trait_ref)
+            let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
                 .filter(|upcast_trait_ref| {
-                    this.probe(|this, _| {
+                    self.infcx.probe(|_| {
                         let upcast_trait_ref = upcast_trait_ref.clone();
-                        this.match_poly_trait_ref(obligation, upcast_trait_ref)
+                        self.match_poly_trait_ref(obligation, upcast_trait_ref)
                             .is_ok()
                     })
                 })
@@ -2352,7 +2257,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 // See if we can toss out `victim` based on specialization.
                 // This requires us to know *for sure* that the `other` impl applies
                 // i.e., EvaluatedToOk:
-                if other.evaluation == EvaluatedToOk {
+                if other.evaluation.must_apply_modulo_regions() {
                     match victim.candidate {
                         ImplCandidate(victim_def) => {
                             let tcx = self.tcx().global_tcx();
@@ -2379,7 +2284,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     ParamCandidate(ref cand) => {
                         // Prefer these to a global where-clause bound
                         // (see issue #50825)
-                        is_global(cand) && other.evaluation == EvaluatedToOk
+                        is_global(cand) && other.evaluation.must_apply_modulo_regions()
                     }
                     _ => false,
                 }
@@ -2680,20 +2585,20 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 // binder moved -\
                 let ty: ty::Binder<Ty<'tcx>> = ty::Binder::bind(ty); // <----/
 
-                self.in_snapshot(|this, snapshot| {
-                    let (skol_ty, placeholder_map) = this.infcx()
+                self.infcx.in_snapshot(|_| {
+                    let (skol_ty, _) = self.infcx
                         .replace_bound_vars_with_placeholders(&ty);
                     let Normalized {
                         value: normalized_ty,
                         mut obligations,
                     } = project::normalize_with_depth(
-                        this,
+                        self,
                         param_env,
                         cause.clone(),
                         recursion_depth,
                         &skol_ty,
                     );
-                    let skol_obligation = this.tcx().predicate_for_trait_def(
+                    let skol_obligation = self.tcx().predicate_for_trait_def(
                         param_env,
                         cause.clone(),
                         trait_def_id,
@@ -2702,8 +2607,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         &[],
                     );
                     obligations.push(skol_obligation);
-                    this.infcx()
-                        .plug_leaks(placeholder_map, snapshot, obligations)
+                    obligations
                 })
             })
             .collect()
@@ -2794,9 +2698,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
     }
 
     fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) {
-        self.in_snapshot(|this, snapshot| {
+        self.infcx.in_snapshot(|_| {
             let result =
-                this.match_projection_obligation_against_definition_bounds(obligation, snapshot);
+                self.match_projection_obligation_against_definition_bounds(obligation);
             assert!(result);
         })
     }
@@ -2913,19 +2817,17 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             nested,
         );
 
-        let trait_obligations: Vec<PredicateObligation<'_>> = self.in_snapshot(|this, snapshot| {
+        let trait_obligations: Vec<PredicateObligation<'_>> = self.infcx.in_snapshot(|_| {
             let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
-            let (trait_ref, placeholder_map) = this.infcx()
+            let (trait_ref, _) = self.infcx
                 .replace_bound_vars_with_placeholders(&poly_trait_ref);
             let cause = obligation.derived_cause(ImplDerivedObligation);
-            this.impl_or_trait_obligations(
+            self.impl_or_trait_obligations(
                 cause,
                 obligation.recursion_depth + 1,
                 obligation.param_env,
                 trait_def_id,
                 &trait_ref.substs,
-                placeholder_map,
-                snapshot,
             )
         });
 
@@ -2950,18 +2852,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
 
         // First, create the substitutions by matching the impl again,
         // this time not in a probe.
-        self.in_snapshot(|this, snapshot| {
-            let (substs, placeholder_map) = this.rematch_impl(impl_def_id, obligation, snapshot);
+        self.infcx.in_snapshot(|_| {
+            let substs = self.rematch_impl(impl_def_id, obligation);
             debug!("confirm_impl_candidate: substs={:?}", substs);
             let cause = obligation.derived_cause(ImplDerivedObligation);
-            this.vtable_impl(
+            self.vtable_impl(
                 impl_def_id,
                 substs,
                 cause,
                 obligation.recursion_depth + 1,
                 obligation.param_env,
-                placeholder_map,
-                snapshot,
             )
         })
     }
@@ -2973,12 +2873,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         cause: ObligationCause<'tcx>,
         recursion_depth: usize,
         param_env: ty::ParamEnv<'tcx>,
-        placeholder_map: infer::PlaceholderMap<'tcx>,
-        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
     ) -> VtableImplData<'tcx, PredicateObligation<'tcx>> {
         debug!(
-            "vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={}, placeholder_map={:?})",
-            impl_def_id, substs, recursion_depth, placeholder_map
+            "vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={})",
+            impl_def_id, substs, recursion_depth,
         );
 
         let mut impl_obligations = self.impl_or_trait_obligations(
@@ -2987,8 +2885,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             param_env,
             impl_def_id,
             &substs.value,
-            placeholder_map,
-            snapshot,
         );
 
         debug!(
@@ -3041,7 +2937,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // reported an ambiguity. (When we do find a match, also
             // record it for later.)
             let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(
-                |&t| match self.commit_if_ok(|this, _| this.match_poly_trait_ref(obligation, t)) {
+                |&t| match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) {
                     Ok(obligations) => {
                         upcast_trait_ref = Some(t);
                         nested.extend(obligations);
@@ -3117,21 +3013,19 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             obligation, alias_def_id
         );
 
-        self.in_snapshot(|this, snapshot| {
-            let (predicate, placeholder_map) = this.infcx()
+        self.infcx.in_snapshot(|_| {
+            let (predicate, _) = self.infcx()
                 .replace_bound_vars_with_placeholders(&obligation.predicate);
             let trait_ref = predicate.trait_ref;
             let trait_def_id = trait_ref.def_id;
             let substs = trait_ref.substs;
 
-            let trait_obligations = this.impl_or_trait_obligations(
+            let trait_obligations = self.impl_or_trait_obligations(
                 obligation.cause.clone(),
                 obligation.recursion_depth,
                 obligation.param_env,
                 trait_def_id,
                 &substs,
-                placeholder_map,
-                snapshot,
             );
 
             debug!(
@@ -3341,10 +3235,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                         );
                     tcx.mk_existential_predicates(iter)
                 });
-                let new_trait = tcx.mk_dynamic(existential_predicates, r_b);
+                let source_trait = tcx.mk_dynamic(existential_predicates, r_b);
                 let InferOk { obligations, .. } = self.infcx
                     .at(&obligation.cause, obligation.param_env)
-                    .eq(target, new_trait)
+                    .sup(target, source_trait)
                     .map_err(|_| Unimplemented)?;
                 nested.extend(obligations);
 
@@ -3546,13 +3440,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         &mut self,
         impl_def_id: DefId,
         obligation: &TraitObligation<'tcx>,
-        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
-    ) -> (
-        Normalized<'tcx, &'tcx Substs<'tcx>>,
-        infer::PlaceholderMap<'tcx>,
-    ) {
-        match self.match_impl(impl_def_id, obligation, snapshot) {
-            Ok((substs, placeholder_map)) => (substs, placeholder_map),
+    ) -> Normalized<'tcx, &'tcx Substs<'tcx>> {
+        match self.match_impl(impl_def_id, obligation) {
+            Ok(substs) => substs,
             Err(()) => {
                 bug!(
                     "Impl {:?} was matchable against {:?} but now is not",
@@ -3567,14 +3457,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         &mut self,
         impl_def_id: DefId,
         obligation: &TraitObligation<'tcx>,
-        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
-    ) -> Result<
-        (
-            Normalized<'tcx, &'tcx Substs<'tcx>>,
-            infer::PlaceholderMap<'tcx>,
-        ),
-        (),
-    > {
+    ) -> Result<Normalized<'tcx, &'tcx Substs<'tcx>>, ()> {
         let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
 
         // Before we create the substitutions and everything, first
@@ -3584,7 +3467,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             return Err(());
         }
 
-        let (skol_obligation, placeholder_map) = self.infcx()
+        let (skol_obligation, _) = self.infcx()
             .replace_bound_vars_with_placeholders(&obligation.predicate);
         let skol_obligation_trait_ref = skol_obligation.trait_ref;
 
@@ -3616,22 +3499,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?;
         nested_obligations.extend(obligations);
 
-        if let Err(e) =
-            self.infcx
-                .leak_check(false, obligation.cause.span, &placeholder_map, snapshot)
-        {
-            debug!("match_impl: failed leak check due to `{}`", e);
-            return Err(());
-        }
-
         debug!("match_impl: success impl_substs={:?}", impl_substs);
-        Ok((
-            Normalized {
-                value: impl_substs,
-                obligations: nested_obligations,
-            },
-            placeholder_map,
-        ))
+        Ok(Normalized {
+            value: impl_substs,
+            obligations: nested_obligations,
+        })
     }
 
     fn fast_reject_trait_refs(
@@ -3787,8 +3659,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         def_id: DefId,         // of impl or trait
         substs: &Substs<'tcx>, // for impl or trait
-        placeholder_map: infer::PlaceholderMap<'tcx>,
-        snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
     ) -> Vec<PredicateObligation<'tcx>> {
         debug!("impl_or_trait_obligations(def_id={:?})", def_id);
         let tcx = self.tcx();
@@ -3850,8 +3720,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             let mut seen = FxHashSet::default();
             predicates.retain(|i| seen.insert(i.clone()));
         }
-        self.infcx()
-            .plug_leaks(placeholder_map, snapshot, predicates)
+
+        predicates
     }
 }
 
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index f5d68ddb5bd..63f52a34dfa 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -15,6 +15,7 @@ use hir::def_id::DefId;
 use infer::{InferCtxt, InferOk};
 use lint;
 use traits::{self, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine};
+use traits::coherence;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
 use syntax_pos::DUMMY_SP;
@@ -32,6 +33,7 @@ pub struct OverlapError {
     pub trait_desc: String,
     pub self_desc: Option<String>,
     pub intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
+    pub involves_placeholder: bool,
 }
 
 /// Given a subst for the requested impl, translate it to a subst
@@ -370,6 +372,10 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(
                     cause.add_intercrate_ambiguity_hint(&mut err);
                 }
 
+                if overlap.involves_placeholder {
+                    coherence::add_placeholder_note(&mut err);
+                }
+
                 err.emit();
             }
         } else {
diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs
index fd68917e539..db3547b2b74 100644
--- a/src/librustc/traits/specialize/specialization_graph.rs
+++ b/src/librustc/traits/specialize/specialization_graph.rs
@@ -164,6 +164,7 @@ impl<'a, 'gcx, 'tcx> Children {
                         None
                     },
                     intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
+                    involves_placeholder: overlap.involves_placeholder,
                 }
             };
 
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs
index 6b931e39a05..76e102d88d7 100644
--- a/src/librustc/ty/error.rs
+++ b/src/librustc/ty/error.rs
@@ -1,5 +1,5 @@
 use hir::def_id::DefId;
-use ty::{self, BoundRegion, Region, Ty, TyCtxt};
+use ty::{self, Region, Ty, TyCtxt};
 use std::borrow::Cow;
 use std::fmt;
 use rustc_target::spec::abi;
@@ -9,7 +9,7 @@ use syntax_pos::Span;
 
 use hir;
 
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub struct ExpectedFound<T> {
     pub expected: T,
     pub found: T,
@@ -27,8 +27,7 @@ pub enum TypeError<'tcx> {
     ArgCount,
 
     RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
-    RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>),
-    RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>),
+    RegionsPlaceholderMismatch,
 
     Sorts(ExpectedFound<Ty<'tcx>>),
     IntMismatch(ExpectedFound<ty::IntVarValue>),
@@ -102,17 +101,8 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
             RegionsDoesNotOutlive(..) => {
                 write!(f, "lifetime mismatch")
             }
-            RegionsInsufficientlyPolymorphic(br, _) => {
-                write!(f,
-                       "expected bound lifetime parameter{}{}, found concrete lifetime",
-                       if br.is_named() { " " } else { "" },
-                       br)
-            }
-            RegionsOverlyPolymorphic(br, _) => {
-                write!(f,
-                       "expected concrete lifetime, found bound lifetime parameter{}{}",
-                       if br.is_named() { " " } else { "" },
-                       br)
+            RegionsPlaceholderMismatch => {
+                write!(f, "one type is more general than the other")
             }
             Sorts(values) => ty::tls::with(|tcx| {
                 report_maybe_different(f, &values.expected.sort_string(tcx),
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index 842aea07614..4d026b97233 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -382,7 +382,7 @@ define_queries! { <'tcx>
         /// might want to use `reveal_all()` method to change modes.
         [] fn param_env: ParamEnv(DefId) -> ty::ParamEnv<'tcx>,
 
-        /// Trait selection queries. These are best used by invoking `ty.moves_by_default()`,
+        /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`,
         /// `ty.is_copy()`, etc, since that will prune the environment where possible.
         [] fn is_copy_raw: is_copy_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
         [] fn is_sized_raw: is_sized_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 4755adc4cd1..f9b43f42d52 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -434,12 +434,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             RegionsDoesNotOutlive(a, b) => {
                 return tcx.lift(&(a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b))
             }
-            RegionsInsufficientlyPolymorphic(a, b) => {
-                return tcx.lift(&b).map(|b| RegionsInsufficientlyPolymorphic(a, b))
-            }
-            RegionsOverlyPolymorphic(a, b) => {
-                return tcx.lift(&b).map(|b| RegionsOverlyPolymorphic(a, b))
-            }
+            RegionsPlaceholderMismatch => RegionsPlaceholderMismatch,
             IntMismatch(x) => IntMismatch(x),
             FloatMismatch(x) => FloatMismatch(x),
             Traits(x) => Traits(x),
@@ -1006,8 +1001,7 @@ EnumTypeFoldableImpl! {
         (ty::error::TypeError::FixedArraySize)(x),
         (ty::error::TypeError::ArgCount),
         (ty::error::TypeError::RegionsDoesNotOutlive)(a, b),
-        (ty::error::TypeError::RegionsInsufficientlyPolymorphic)(a, b),
-        (ty::error::TypeError::RegionsOverlyPolymorphic)(a, b),
+        (ty::error::TypeError::RegionsPlaceholderMismatch),
         (ty::error::TypeError::IntMismatch)(x),
         (ty::error::TypeError::FloatMismatch)(x),
         (ty::error::TypeError::Traits)(x),
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index f7adb83f8eb..a2720bdf385 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -1396,6 +1396,13 @@ impl RegionKind {
         }
     }
 
+    pub fn is_placeholder(&self) -> bool {
+        match *self {
+            ty::RePlaceholder(..) => true,
+            _ => false,
+        }
+    }
+
     pub fn bound_at_or_above_binder(&self, index: DebruijnIndex) -> bool {
         match *self {
             ty::ReLateBound(debruijn, _) => debruijn >= index,
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 1d30ccb87b5..ac062a23786 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -203,7 +203,7 @@ impl<'tcx> ty::ParamEnv<'tcx> {
                     let cause = ObligationCause { span, ..ObligationCause::dummy() };
                     let ctx = traits::FulfillmentContext::new();
                     match traits::fully_normalize(&infcx, ctx, cause, self, &ty) {
-                        Ok(ty) => if infcx.type_moves_by_default(self, ty, span) {
+                        Ok(ty) => if !infcx.type_is_copy_modulo_regions(self, ty, span) {
                             infringing.push(field);
                         }
                         Err(errors) => {
@@ -621,14 +621,27 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 }
 
 impl<'a, 'tcx> ty::TyS<'tcx> {
-    pub fn moves_by_default(&'tcx self,
-                            tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                            param_env: ty::ParamEnv<'tcx>,
-                            span: Span)
-                            -> bool {
-        !tcx.at(span).is_copy_raw(param_env.and(self))
+    /// Checks whether values of this type `T` are *moved* or *copied*
+    /// when referenced -- this amounts to a check for whether `T:
+    /// Copy`, but note that we **don't** consider lifetimes when
+    /// doing this check. This means that we may generate MIR which
+    /// does copies even when the type actually doesn't satisfy the
+    /// full requirements for the `Copy` trait (cc #29149) -- this
+    /// winds up being reported as an error during NLL borrow check.
+    pub fn is_copy_modulo_regions(&'tcx self,
+                                  tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                  param_env: ty::ParamEnv<'tcx>,
+                                  span: Span)
+                                  -> bool {
+        tcx.at(span).is_copy_raw(param_env.and(self))
     }
 
+    /// Checks whether values of this type `T` have a size known at
+    /// compile time (i.e., whether `T: Sized`). Lifetimes are ignored
+    /// for the purposes of this check, so it can be an
+    /// over-approximation in generic contexts, where one can have
+    /// strange rules like `<T as Foo<'static>>::Bar: Sized` that
+    /// actually carry lifetime requirements.
     pub fn is_sized(&'tcx self,
                     tcx_at: TyCtxtAt<'a, 'tcx, 'tcx>,
                     param_env: ty::ParamEnv<'tcx>)-> bool
@@ -636,6 +649,13 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
         tcx_at.is_sized_raw(param_env.and(self))
     }
 
+    /// Checks whether values of this type `T` implement the `Freeze`
+    /// trait -- frozen types are those that do not contain a
+    /// `UnsafeCell` anywhere.  This is a language concept used to
+    /// distinguish "true immutability", which is relevant to
+    /// optimization as well as the rules around static values. Note
+    /// that the `Freeze` trait is not exposed to end users and is
+    /// effectively an implementation detail.
     pub fn is_freeze(&'tcx self,
                      tcx: TyCtxt<'a, 'tcx, 'tcx>,
                      param_env: ty::ParamEnv<'tcx>,
@@ -851,11 +871,13 @@ fn is_copy_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::CopyTraitLangItem);
     tcx.infer_ctxt()
-       .enter(|infcx| traits::type_known_to_meet_bound(&infcx,
-                                                       param_env,
-                                                       ty,
-                                                       trait_def_id,
-                                                       DUMMY_SP))
+        .enter(|infcx| traits::type_known_to_meet_bound_modulo_regions(
+            &infcx,
+            param_env,
+            ty,
+            trait_def_id,
+            DUMMY_SP,
+        ))
 }
 
 fn is_sized_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -865,11 +887,13 @@ fn is_sized_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::SizedTraitLangItem);
     tcx.infer_ctxt()
-       .enter(|infcx| traits::type_known_to_meet_bound(&infcx,
-                                                       param_env,
-                                                       ty,
-                                                       trait_def_id,
-                                                       DUMMY_SP))
+        .enter(|infcx| traits::type_known_to_meet_bound_modulo_regions(
+            &infcx,
+            param_env,
+            ty,
+            trait_def_id,
+            DUMMY_SP,
+        ))
 }
 
 fn is_freeze_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -879,11 +903,13 @@ fn is_freeze_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(lang_items::FreezeTraitLangItem);
     tcx.infer_ctxt()
-       .enter(|infcx| traits::type_known_to_meet_bound(&infcx,
-                                                       param_env,
-                                                       ty,
-                                                       trait_def_id,
-                                                       DUMMY_SP))
+        .enter(|infcx| traits::type_known_to_meet_bound_modulo_regions(
+            &infcx,
+            param_env,
+            ty,
+            trait_def_id,
+            DUMMY_SP,
+        ))
 }
 
 fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -921,11 +947,11 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         // `ManuallyDrop` doesn't have a destructor regardless of field types.
         ty::Adt(def, _) if Some(def.did) == tcx.lang_items().manually_drop() => false,
 
-        // Issue #22536: We first query type_moves_by_default.  It sees a
+        // Issue #22536: We first query `is_copy_modulo_regions`.  It sees a
         // normalized version of the type, and therefore will definitely
         // know whether the type implements Copy (and thus needs no
         // cleanup/drop/zeroing) ...
-        _ if !ty.moves_by_default(tcx, param_env, DUMMY_SP) => false,
+        _ if ty.is_copy_modulo_regions(tcx, param_env, DUMMY_SP) => false,
 
         // ... (issue #22536 continued) but as an optimization, still use
         // prior logic of asking for the structural "may drop".
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 318d7adb190..79405b12400 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -9,7 +9,7 @@ use ty::{Error, Str, Array, Slice, Float, FnDef, FnPtr};
 use ty::{Param, Bound, RawPtr, Ref, Never, Tuple};
 use ty::{Closure, Generator, GeneratorWitness, Foreign, Projection, Opaque};
 use ty::{Placeholder, UnnormalizedProjection, Dynamic, Int, Uint, Infer};
-use ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, GenericParamCount, GenericParamDefKind};
+use ty::{self, Ty, TyCtxt, TypeFoldable, GenericParamCount, GenericParamDefKind};
 use util::nodemap::FxHashSet;
 
 use std::cell::Cell;
@@ -21,17 +21,163 @@ use syntax::ast::CRATE_NODE_ID;
 use syntax::symbol::{Symbol, InternedString};
 use hir;
 
+/// The "region highlights" are used to control region printing during
+/// specific error messages. When a "region highlight" is enabled, it
+/// gives an alternate way to print specific regions. For now, we
+/// always print those regions using a number, so something like `'0`.
+///
+/// Regions not selected by the region highlight mode are presently
+/// unaffected.
+#[derive(Copy, Clone, Default)]
+pub struct RegionHighlightMode {
+    /// If enabled, when we see the selected region, use `"'N"`
+    /// instead of the ordinary behavior.
+    highlight_regions: [Option<(ty::RegionKind, usize)>; 3],
+
+    /// If enabled, when printing a "free region" that originated from
+    /// the given `ty::BoundRegion`, print it as `'1`. Free regions that would ordinarily
+    /// have names print as normal.
+    ///
+    /// This is used when you have a signature like `fn foo(x: &u32,
+    /// y: &'a u32)` and we want to give a name to the region of the
+    /// reference `x`.
+    highlight_bound_region: Option<(ty::BoundRegion, usize)>,
+}
+
 thread_local! {
     /// Mechanism for highlighting of specific regions for display in NLL region inference errors.
     /// Contains region to highlight and counter for number to use when highlighting.
-    static HIGHLIGHT_REGION_FOR_REGIONVID: Cell<Option<(RegionVid, usize)>> = Cell::new(None)
+    static REGION_HIGHLIGHT_MODE: Cell<RegionHighlightMode> =
+        Cell::new(RegionHighlightMode::default())
 }
 
-thread_local! {
-    /// Mechanism for highlighting of specific regions for display in NLL's 'borrow does not live
-    /// long enough' errors. Contains a region to highlight and a counter to use.
-    static HIGHLIGHT_REGION_FOR_BOUND_REGION: Cell<Option<(ty::BoundRegion, usize)>> =
-        Cell::new(None)
+impl RegionHighlightMode {
+    /// Read and return current region highlight settings (accesses thread-local state).a
+    pub fn get() -> Self {
+        REGION_HIGHLIGHT_MODE.with(|c| c.get())
+    }
+
+    /// Internal helper to update current settings during the execution of `op`.
+    fn set<R>(
+        old_mode: Self,
+        new_mode: Self,
+        op: impl FnOnce() -> R,
+    ) -> R {
+        REGION_HIGHLIGHT_MODE.with(|c| {
+            c.set(new_mode);
+            let result = op();
+            c.set(old_mode);
+            result
+        })
+    }
+
+    /// If `region` and `number` are both `Some`, invoke
+    /// `highlighting_region`. Otherwise, just invoke `op` directly.
+    pub fn maybe_highlighting_region<R>(
+        region: Option<ty::Region<'_>>,
+        number: Option<usize>,
+        op: impl FnOnce() -> R,
+    ) -> R {
+        if let Some(k) = region {
+            if let Some(n) = number {
+                return Self::highlighting_region(k, n, op);
+            }
+        }
+
+        op()
+    }
+
+    /// During the execution of `op`, highlight the region inference
+    /// vairable `vid` as `'N`.  We can only highlight one region vid
+    /// at a time.
+    pub fn highlighting_region<R>(
+        region: ty::Region<'_>,
+        number: usize,
+        op: impl FnOnce() -> R,
+    ) -> R {
+        let old_mode = Self::get();
+        let mut new_mode = old_mode;
+        let first_avail_slot = new_mode.highlight_regions.iter_mut()
+            .filter(|s| s.is_none())
+            .next()
+            .unwrap_or_else(|| {
+                panic!(
+                    "can only highlight {} placeholders at a time",
+                    old_mode.highlight_regions.len(),
+                )
+            });
+        *first_avail_slot = Some((*region, number));
+        Self::set(old_mode, new_mode, op)
+    }
+
+    /// Convenience wrapper for `highlighting_region`
+    pub fn highlighting_region_vid<R>(
+        vid: ty::RegionVid,
+        number: usize,
+        op: impl FnOnce() -> R,
+    ) -> R {
+        Self::highlighting_region(&ty::ReVar(vid), number, op)
+    }
+
+    /// Returns true if any placeholders are highlighted.
+    fn any_region_vids_highlighted(&self) -> bool {
+        Self::get()
+            .highlight_regions
+            .iter()
+            .any(|h| match h {
+                Some((ty::ReVar(_), _)) => true,
+                _ => false,
+            })
+    }
+
+    /// Returns `Some(n)` with the number to use for the given region,
+    /// if any.
+    fn region_highlighted(&self, region: ty::Region<'_>) -> Option<usize> {
+        Self::get()
+            .highlight_regions
+            .iter()
+            .filter_map(|h| match h {
+                Some((r, n)) if r == region => Some(*n),
+                _ => None,
+            })
+            .next()
+    }
+
+    /// During the execution of `op`, highlight the given bound
+    /// region. We can only highlight one bound region at a time.  See
+    /// the field `highlight_bound_region` for more detailed notes.
+    pub fn highlighting_bound_region<R>(
+        br: ty::BoundRegion,
+        number: usize,
+        op: impl FnOnce() -> R,
+    ) -> R {
+        let old_mode = Self::get();
+        assert!(old_mode.highlight_bound_region.is_none());
+        Self::set(
+            old_mode,
+            Self {
+                highlight_bound_region: Some((br, number)),
+                ..old_mode
+            },
+            op,
+        )
+    }
+
+    /// Returns true if any placeholders are highlighted.
+    pub fn any_placeholders_highlighted(&self) -> bool {
+        Self::get()
+            .highlight_regions
+            .iter()
+            .any(|h| match h {
+                Some((ty::RePlaceholder(_), _)) => true,
+                _ => false,
+            })
+    }
+
+    /// Returns `Some(N)` if the placeholder `p` is highlighted to print as `'N`.
+    pub fn placeholder_highlight(&self, p: ty::PlaceholderRegion) -> Option<usize> {
+        self.region_highlighted(&ty::RePlaceholder(p))
+    }
 }
 
 macro_rules! gen_display_debug_body {
@@ -553,42 +699,6 @@ pub fn parameterized<F: fmt::Write>(f: &mut F,
     PrintContext::new().parameterized(f, substs, did, projections)
 }
 
-fn get_highlight_region_for_regionvid() -> Option<(RegionVid, usize)> {
-    HIGHLIGHT_REGION_FOR_REGIONVID.with(|hr| hr.get())
-}
-
-pub fn with_highlight_region_for_regionvid<R>(
-    r: RegionVid,
-    counter: usize,
-    op: impl FnOnce() -> R
-) -> R {
-    HIGHLIGHT_REGION_FOR_REGIONVID.with(|hr| {
-        assert_eq!(hr.get(), None);
-        hr.set(Some((r, counter)));
-        let r = op();
-        hr.set(None);
-        r
-    })
-}
-
-fn get_highlight_region_for_bound_region() -> Option<(ty::BoundRegion, usize)> {
-    HIGHLIGHT_REGION_FOR_BOUND_REGION.with(|hr| hr.get())
-}
-
-pub fn with_highlight_region_for_bound_region<R>(
-    r: ty::BoundRegion,
-    counter: usize,
-    op: impl Fn() -> R
-) -> R {
-    HIGHLIGHT_REGION_FOR_BOUND_REGION.with(|hr| {
-        assert_eq!(hr.get(), None);
-        hr.set(Some((r, counter)));
-        let r = op();
-        hr.set(None);
-        r
-    })
-}
-
 impl<'a, T: Print> Print for &'a T {
     fn print<F: fmt::Write>(&self, f: &mut F, cx: &mut PrintContext) -> fmt::Result {
         (*self).print(f, cx)
@@ -740,7 +850,7 @@ define_print! {
                 return self.print_debug(f, cx);
             }
 
-            if let Some((region, counter)) = get_highlight_region_for_bound_region() {
+            if let Some((region, counter)) = RegionHighlightMode::get().highlight_bound_region {
                 if *self == region {
                     return match *self {
                         BrNamed(_, name) => write!(f, "{}", name),
@@ -769,12 +879,36 @@ define_print! {
 }
 
 define_print! {
+    () ty::PlaceholderRegion, (self, f, cx) {
+        display {
+            if cx.is_verbose {
+                return self.print_debug(f, cx);
+            }
+
+            let highlight = RegionHighlightMode::get();
+            if let Some(counter) = highlight.placeholder_highlight(*self) {
+                write!(f, "'{}", counter)
+            } else if highlight.any_placeholders_highlighted() {
+                write!(f, "'_")
+            } else {
+                write!(f, "{}", self.name)
+            }
+        }
+    }
+}
+
+define_print! {
     () ty::RegionKind, (self, f, cx) {
         display {
-            if cx.is_verbose || get_highlight_region_for_regionvid().is_some() {
+            if cx.is_verbose {
                 return self.print_debug(f, cx);
             }
 
+            // Watch out for region highlights.
+            if let Some(n) = RegionHighlightMode::get().region_highlighted(self) {
+                return write!(f, "'{:?}", n);
+            }
+
             // These printouts are concise.  They do not contain all the information
             // the user might want to diagnose an error, but there is basically no way
             // to fit that into a short string.  Hence the recommendation to use
@@ -784,10 +918,12 @@ define_print! {
                     write!(f, "{}", data.name)
                 }
                 ty::ReLateBound(_, br) |
-                ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
-                ty::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
+                ty::ReFree(ty::FreeRegion { bound_region: br, .. }) => {
                     write!(f, "{}", br)
                 }
+                ty::RePlaceholder(p) => {
+                    write!(f, "{}", p)
+                }
                 ty::ReScope(scope) if cx.identify_regions => {
                     match scope.data {
                         region::ScopeData::Node =>
@@ -806,11 +942,16 @@ define_print! {
                         ),
                     }
                 }
-                ty::ReVar(region_vid) if cx.identify_regions => {
-                    write!(f, "'{}rv", region_vid.index())
+                ty::ReVar(region_vid) => {
+                    if RegionHighlightMode::get().any_region_vids_highlighted() {
+                        write!(f, "{:?}", region_vid)
+                    } else if cx.identify_regions {
+                        write!(f, "'{}rv", region_vid.index())
+                    } else {
+                        Ok(())
+                    }
                 }
                 ty::ReScope(_) |
-                ty::ReVar(_) |
                 ty::ReErased => Ok(()),
                 ty::ReStatic => write!(f, "'static"),
                 ty::ReEmpty => write!(f, "'<empty>"),
@@ -939,13 +1080,10 @@ impl fmt::Debug for ty::FloatVid {
 
 impl fmt::Debug for ty::RegionVid {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if let Some((region, counter)) = get_highlight_region_for_regionvid() {
-            debug!("RegionVid.fmt: region={:?} self={:?} counter={:?}", region, self, counter);
-            return if *self == region {
-                write!(f, "'{:?}", counter)
-            } else {
-                write!(f, "'_")
-            }
+        if let Some(counter) = RegionHighlightMode::get().region_highlighted(&ty::ReVar(*self)) {
+            return write!(f, "'{:?}", counter);
+        } else if RegionHighlightMode::get().any_region_vids_highlighted() {
+            return write!(f, "'_");
         }
 
         write!(f, "'_#{}r", self.index())
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index a5f7d676862..9c027f110eb 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -7,7 +7,6 @@ use errors::{DiagnosticBuilder, Level};
 use rustc::hir;
 use rustc::hir::map as hir_map;
 use rustc::infer::outlives::env::OutlivesEnvironment;
-use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::infer::{self, InferOk, InferResult, SuppressRegionErrors};
 use rustc::middle::region;
 use rustc::session::config::{OutputFilenames, OutputTypes};
@@ -26,7 +25,6 @@ use syntax::ast;
 use syntax::feature_gate::UnstableFeatures;
 use syntax::source_map::{FileName, FilePathMapping, SourceMap};
 use syntax::symbol::Symbol;
-use syntax_pos::DUMMY_SP;
 
 use std::path::PathBuf;
 use std::sync::mpsc;
@@ -431,17 +429,6 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
             }
         }
     }
-
-    /// Checks that `t1 <: t2` is false (this may register additional
-    /// region checks).
-    pub fn check_not_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) {
-        match self.sub(t1, t2) {
-            Err(_) => {}
-            Ok(_) => {
-                panic!("unexpected success computing sub({:?},{:?})", t1, t2);
-            }
-        }
-    }
 }
 
 #[test]
@@ -471,25 +458,6 @@ fn contravariant_region_ptr_err() {
 }
 
 #[test]
-fn sub_free_bound_false() {
-    //! Test that:
-    //!
-    //!     fn(&'a isize) <: for<'b> fn(&'b isize)
-    //!
-    //! *does not* hold.
-
-    test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| {
-        env.create_simple_region_hierarchy();
-        let t_rptr_free1 = env.t_rptr_free(1);
-        let t_rptr_bound1 = env.t_rptr_late_bound(1);
-        env.check_not_sub(
-            env.t_fn(&[t_rptr_free1], env.tcx().types.isize),
-            env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
-        );
-    })
-}
-
-#[test]
 fn sub_bound_free_true() {
     //! Test that:
     //!
@@ -508,25 +476,6 @@ fn sub_bound_free_true() {
     })
 }
 
-#[test]
-fn sub_free_bound_false_infer() {
-    //! Test that:
-    //!
-    //!     fn(_#1) <: for<'b> fn(&'b isize)
-    //!
-    //! does NOT hold for any instantiation of `_#1`.
-
-    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_infer1 = env.infcx
-            .next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
-        let t_rptr_bound1 = env.t_rptr_late_bound(1);
-        env.check_not_sub(
-            env.t_fn(&[t_infer1], env.tcx().types.isize),
-            env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
-        );
-    })
-}
-
 /// Test substituting a bound region into a function, which introduces another level of binding.
 /// This requires adjusting the Debruijn index.
 #[test]
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index cd4cbcc13a6..27b65b4ec48 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -540,7 +540,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations {
             return;
         }
         let param_env = ty::ParamEnv::empty();
-        if !ty.moves_by_default(cx.tcx, param_env, item.span) {
+        if ty.is_copy_modulo_regions(cx.tcx, param_env, item.span) {
             return;
         }
         if param_env.can_type_implement_copy(cx.tcx, ty).is_ok() {
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 6a5b5d172bb..83cd28bbdc5 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -12,7 +12,7 @@ use rustc::mir::{
     TerminatorKind, VarBindingForm,
 };
 use rustc::ty::{self, DefIdTree};
-use rustc::util::ppaux::with_highlight_region_for_bound_region;
+use rustc::util::ppaux::RegionHighlightMode;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::sync::Lrc;
@@ -2177,7 +2177,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
                 ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
                 _,
                 _,
-            ) => with_highlight_region_for_bound_region(*br, counter, || ty.to_string()),
+            ) => RegionHighlightMode::highlighting_bound_region(*br, counter, || ty.to_string()),
             _ => ty.to_string(),
         }
     }
@@ -2189,7 +2189,11 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
             ty::TyKind::Ref(region, _, _) => match region {
                 ty::RegionKind::ReLateBound(_, br)
                 | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
-                    with_highlight_region_for_bound_region(*br, counter, || region.to_string())
+                    RegionHighlightMode::highlighting_bound_region(
+                        *br,
+                        counter,
+                        || region.to_string(),
+                    )
                 }
                 _ => region.to_string(),
             },
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
index 963d4ab3a54..bff80155112 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
@@ -8,7 +8,7 @@ use rustc::infer::InferCtxt;
 use rustc::mir::Mir;
 use rustc::ty::subst::{Substs, UnpackedKind};
 use rustc::ty::{self, RegionKind, RegionVid, Ty, TyCtxt};
-use rustc::util::ppaux::with_highlight_region_for_regionvid;
+use rustc::util::ppaux::RegionHighlightMode;
 use rustc_errors::DiagnosticBuilder;
 use syntax::ast::{Name, DUMMY_NODE_ID};
 use syntax::symbol::keywords;
@@ -396,7 +396,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         argument_ty: Ty<'tcx>,
         counter: &mut usize,
     ) -> Option<RegionName> {
-        let type_name = with_highlight_region_for_regionvid(needle_fr, *counter, || {
+        let type_name = RegionHighlightMode::highlighting_region_vid(needle_fr, *counter, || {
             infcx.extract_type_name(&argument_ty)
         });
 
@@ -673,8 +673,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             return None;
         }
 
-        let type_name = with_highlight_region_for_regionvid(
-            fr, *counter, || infcx.extract_type_name(&return_ty));
+        let type_name = RegionHighlightMode::highlighting_region_vid(
+            fr, *counter, || infcx.extract_type_name(&return_ty),
+        );
 
         let mir_node_id = tcx.hir().as_local_node_id(mir_def_id).expect("non-local mir");
 
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 8bb67521f44..b3e1237a450 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -503,13 +503,17 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
                 substs: tcx.mk_substs_trait(place_ty.to_ty(tcx), &[]),
             };
 
-            // In order to have a Copy operand, the type T of the value must be Copy. Note that we
-            // prove that T: Copy, rather than using the type_moves_by_default test. This is
-            // important because type_moves_by_default ignores the resulting region obligations and
-            // assumes they pass. This can result in bounds from Copy impls being unsoundly ignored
-            // (e.g., #29149). Note that we decide to use Copy before knowing whether the bounds
-            // fully apply: in effect, the rule is that if a value of some type could implement
-            // Copy, then it must.
+            // In order to have a Copy operand, the type T of the
+            // value must be Copy. Note that we prove that T: Copy,
+            // rather than using the `is_copy_modulo_regions`
+            // test. This is important because
+            // `is_copy_modulo_regions` ignores the resulting region
+            // obligations and assumes they pass. This can result in
+            // bounds from Copy impls being unsoundly ignored (e.g.,
+            // #29149). Note that we decide to use Copy before knowing
+            // whether the bounds fully apply: in effect, the rule is
+            // that if a value of some type could implement Copy, then
+            // it must.
             self.cx.prove_trait_ref(
                 trait_ref,
                 location.to_locations(),
diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs
index 472f05a101f..5b20d412f0d 100644
--- a/src/librustc_mir/build/misc.rs
+++ b/src/librustc_mir/build/misc.rs
@@ -71,7 +71,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> {
         let tcx = self.hir.tcx();
         let ty = place.ty(&self.local_decls, tcx).to_ty(tcx);
-        if self.hir.type_moves_by_default(ty, DUMMY_SP) {
+        if !self.hir.type_is_copy_modulo_regions(ty, DUMMY_SP) {
             Operand::Move(place)
         } else {
             Operand::Copy(place)
diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs
index 60c8022a374..07f3c38c62c 100644
--- a/src/librustc_mir/hair/cx/mod.rs
+++ b/src/librustc_mir/hair/cx/mod.rs
@@ -223,8 +223,8 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
         self.check_overflow
     }
 
-    pub fn type_moves_by_default(&self, ty: Ty<'tcx>, span: Span) -> bool {
-        self.infcx.type_moves_by_default(self.param_env, ty, span)
+    pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool {
+        self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span)
     }
 }
 
diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs
index f758e488877..c104af7a7d8 100644
--- a/src/librustc_mir/hair/pattern/check_match.rs
+++ b/src/librustc_mir/hair/pattern/check_match.rs
@@ -545,7 +545,7 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
                     match bm {
                         ty::BindByValue(..) => {
                             let pat_ty = cx.tables.node_id_to_type(p.hir_id);
-                            if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) {
+                            if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
                                 check_move(p, sub.as_ref().map(|p| &**p), span_vec);
                             }
                         }
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 4c123d4a44b..2aa44cc181a 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -310,7 +310,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     debug!("build_clone_shim(def_id={:?})", def_id);
 
     let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty);
-    let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), builder.span);
+    let is_copy = self_ty.is_copy_modulo_regions(tcx, tcx.param_env(def_id), builder.span);
 
     let dest = Place::Local(RETURN_PLACE);
     let src = Place::Local(Local::new(1+0)).deref();
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index 0b0845ef945..eb151b56bed 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -224,8 +224,11 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                                         "non-field projection {:?} from union?",
                                         place)
                                 };
-                                if elem_ty.moves_by_default(self.tcx, self.param_env,
-                                                            self.source_info.span) {
+                                if !elem_ty.is_copy_modulo_regions(
+                                    self.tcx,
+                                    self.param_env,
+                                    self.source_info.span,
+                                ) {
                                     self.require_unsafe(
                                         "assignment to non-`Copy` union field",
                                         "the previous content of the field will be dropped, which \
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 6ad707e3d2c..9d418704f48 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -88,7 +88,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             return Err(ErrorReported);
         }
 
-        if self.type_is_known_to_be_sized(t, span) {
+        if self.type_is_known_to_be_sized_modulo_regions(t, span) {
             return Ok(Some(PointerKind::Thin));
         }
 
@@ -397,7 +397,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
                self.expr_ty,
                self.cast_ty);
 
-        if !fcx.type_is_known_to_be_sized(self.cast_ty, self.span) {
+        if !fcx.type_is_known_to_be_sized_modulo_regions(self.cast_ty, self.span) {
             self.report_cast_to_unsized_type(fcx);
         } else if self.expr_ty.references_error() || self.cast_ty.references_error() {
             // No sense in giving duplicate error messages
@@ -618,8 +618,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
 }
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
-    fn type_is_known_to_be_sized(&self, ty: Ty<'tcx>, span: Span) -> bool {
+    fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool {
         let lang_item = self.tcx.require_lang_item(lang_items::SizedTraitLangItem);
-        traits::type_known_to_meet_bound(self, self.param_env, ty, lang_item, span)
+        traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span)
     }
 }
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 84ca77663a6..7a788f2de7f 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -353,9 +353,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
                         // Maybe add `*`? Only if `T: Copy`.
                         _ => {
-                            if !self.infcx.type_moves_by_default(self.param_env,
-                                                                checked,
-                                                                sp) {
+                            if self.infcx.type_is_copy_modulo_regions(self.param_env,
+                                                                      checked,
+                                                                      sp) {
                                 // do not suggest if the span comes from a macro (#52783)
                                 if let (Ok(code),
                                         true) = (cm.span_to_snippet(sp), sp == expr.span) {
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 8cf97970a15..7c871601af3 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -262,9 +262,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             let mut suggested_deref = false;
                             if let Ref(_, mut rty, _) = lhs_ty.sty {
                                 if {
-                                    !self.infcx.type_moves_by_default(self.param_env,
-                                                                        rty,
-                                                                        lhs_expr.span) &&
+                                    self.infcx.type_is_copy_modulo_regions(self.param_env,
+                                                                           rty,
+                                                                           lhs_expr.span) &&
                                         self.lookup_op_method(rty,
                                                               &[rhs_ty],
                                                               Op::Binary(op, is_assign))
@@ -334,9 +334,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             let mut suggested_deref = false;
                             if let Ref(_, mut rty, _) = lhs_ty.sty {
                                 if {
-                                    !self.infcx.type_moves_by_default(self.param_env,
-                                                                      rty,
-                                                                      lhs_expr.span) &&
+                                    self.infcx.type_is_copy_modulo_regions(self.param_env,
+                                                                           rty,
+                                                                           lhs_expr.span) &&
                                         self.lookup_op_method(rty,
                                                               &[rhs_ty],
                                                               Op::Binary(op, is_assign))
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 65539d3b107..53e44d53e6a 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -873,7 +873,7 @@ fn receiver_is_valid<'fcx, 'tcx, 'gcx>(
             trait_ref.to_predicate()
         );
 
-        if !fcx.predicate_must_hold(&obligation) {
+        if !fcx.predicate_must_hold_modulo_regions(&obligation) {
             debug!("receiver_is_valid: type `{:?}` does not implement `Receiver` trait",
                 receiver_ty);
             return false
diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
index e568320ba8e..8d27a77b29c 100644
--- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs
+++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
@@ -73,6 +73,10 @@ impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
                         cause.add_intercrate_ambiguity_hint(&mut err);
                     }
 
+                    if overlap.involves_placeholder {
+                        traits::add_placeholder_note(&mut err);
+                    }
+
                     err.emit();
                 }
             }
diff --git a/src/test/run-pass/coherence/coherence-subtyping.rs b/src/test/run-pass/coherence/coherence-subtyping.rs
deleted file mode 100644
index b7aa57b5c31..00000000000
--- a/src/test/run-pass/coherence/coherence-subtyping.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-// run-pass
-// Test that two distinct impls which match subtypes of one another
-// yield coherence errors (or not) depending on the variance.
-
-trait Contravariant {
-    fn foo(&self) { }
-}
-
-impl Contravariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
-}
-
-impl Contravariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-trait Covariant {
-    fn foo(&self) { }
-}
-
-impl Covariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
-}
-
-impl Covariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-trait Invariant {
-    fn foo(&self) { }
-}
-
-impl Invariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
-}
-
-impl Invariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
-}
-
-fn main() { }
diff --git a/src/test/ui/associated-types/associated-types-eq-hr.rs b/src/test/ui/associated-types/associated-types-eq-hr.rs
index d0245a07e40..20fa1e7a48d 100644
--- a/src/test/ui/associated-types/associated-types-eq-hr.rs
+++ b/src/test/ui/associated-types/associated-types-eq-hr.rs
@@ -77,23 +77,33 @@ fn tuple_four<T>()
     // not ok for tuple, two lifetimes, and lifetime matching is invariant
 }
 
-pub fn main() {
+pub fn call_foo() {
     foo::<IntStruct>();
     foo::<UintStruct>(); //~ ERROR type mismatch
+}
 
+pub fn call_bar() {
     bar::<IntStruct>(); //~ ERROR type mismatch
     bar::<UintStruct>();
+}
 
+pub fn call_tuple_one() {
     tuple_one::<Tuple>();
-    //~^ ERROR E0277
-    //~| ERROR type mismatch
+    //~^ ERROR not general enough
+}
 
+pub fn call_tuple_two() {
     tuple_two::<Tuple>();
-    //~^ ERROR E0277
-    //~| ERROR type mismatch
+    //~^ ERROR not general enough
+}
 
+pub fn call_tuple_three() {
     tuple_three::<Tuple>();
+}
 
+pub fn call_tuple_four() {
     tuple_four::<Tuple>();
-    //~^ ERROR E0277
+    //~^ ERROR not general enough
 }
+
+fn main() { }
diff --git a/src/test/ui/associated-types/associated-types-eq-hr.stderr b/src/test/ui/associated-types/associated-types-eq-hr.stderr
index 5e04dc5f8c5..d3eaa894b50 100644
--- a/src/test/ui/associated-types/associated-types-eq-hr.stderr
+++ b/src/test/ui/associated-types/associated-types-eq-hr.stderr
@@ -17,7 +17,7 @@ LL | | }
    | |_^
 
 error[E0271]: type mismatch resolving `for<'x> <IntStruct as TheTrait<&'x isize>>::A == &'x usize`
-  --> $DIR/associated-types-eq-hr.rs:84:5
+  --> $DIR/associated-types-eq-hr.rs:86:5
    |
 LL |     bar::<IntStruct>(); //~ ERROR type mismatch
    |     ^^^^^^^^^^^^^^^^ expected isize, found usize
@@ -34,93 +34,36 @@ LL | |     // ok for UintStruct, but not IntStruct
 LL | | }
    | |_^
 
-error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied
-  --> $DIR/associated-types-eq-hr.rs:87:5
-   |
-LL |     tuple_one::<Tuple>();
-   |     ^^^^^^^^^^^^^^^^^^ the trait `for<'x, 'y> TheTrait<(&'x isize, &'y isize)>` is not implemented for `Tuple`
-   |
-   = help: the following implementations were found:
-             <Tuple as TheTrait<(&'a isize, &'a isize)>>
-note: required by `tuple_one`
-  --> $DIR/associated-types-eq-hr.rs:56:1
-   |
-LL | / fn tuple_one<T>()
-LL | |     where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize>
-LL | | {
-LL | |     // not ok for tuple, two lifetimes and we pick first
-LL | | }
-   | |_^
-
-error[E0271]: type mismatch resolving `for<'x, 'y> <Tuple as TheTrait<(&'x isize, &'y isize)>>::A == &'x isize`
-  --> $DIR/associated-types-eq-hr.rs:87:5
-   |
-LL |     tuple_one::<Tuple>();
-   |     ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'x, found concrete lifetime
-   |
-note: required by `tuple_one`
-  --> $DIR/associated-types-eq-hr.rs:56:1
-   |
-LL | / fn tuple_one<T>()
-LL | |     where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize>
-LL | | {
-LL | |     // not ok for tuple, two lifetimes and we pick first
-LL | | }
-   | |_^
-
-error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied
+error: implementation of `TheTrait` is not general enough
   --> $DIR/associated-types-eq-hr.rs:91:5
    |
-LL |     tuple_two::<Tuple>();
-   |     ^^^^^^^^^^^^^^^^^^ the trait `for<'x, 'y> TheTrait<(&'x isize, &'y isize)>` is not implemented for `Tuple`
-   |
-   = help: the following implementations were found:
-             <Tuple as TheTrait<(&'a isize, &'a isize)>>
-note: required by `tuple_two`
-  --> $DIR/associated-types-eq-hr.rs:62:1
+LL |     tuple_one::<Tuple>();
+   |     ^^^^^^^^^^^^^^^^^^
    |
-LL | / fn tuple_two<T>()
-LL | |     where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>
-LL | | {
-LL | |     // not ok for tuple, two lifetimes and we pick second
-LL | | }
-   | |_^
+   = note: Due to a where-clause on `tuple_one`,
+   = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>` for any two lifetimes `'0` and `'1`
+   = note: but `Tuple` only implements `TheTrait<(&'2 isize, &'2 isize)>` for some lifetime `'2`
 
-error[E0271]: type mismatch resolving `for<'x, 'y> <Tuple as TheTrait<(&'x isize, &'y isize)>>::A == &'y isize`
-  --> $DIR/associated-types-eq-hr.rs:91:5
+error: implementation of `TheTrait` is not general enough
+  --> $DIR/associated-types-eq-hr.rs:96:5
    |
 LL |     tuple_two::<Tuple>();
-   |     ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'x, found concrete lifetime
-   |
-note: required by `tuple_two`
-  --> $DIR/associated-types-eq-hr.rs:62:1
+   |     ^^^^^^^^^^^^^^^^^^
    |
-LL | / fn tuple_two<T>()
-LL | |     where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>
-LL | | {
-LL | |     // not ok for tuple, two lifetimes and we pick second
-LL | | }
-   | |_^
+   = note: Due to a where-clause on `tuple_two`,
+   = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>` for any two lifetimes `'0` and `'1`
+   = note: but `Tuple` only implements `TheTrait<(&'2 isize, &'2 isize)>` for some lifetime `'2`
 
-error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied
-  --> $DIR/associated-types-eq-hr.rs:97:5
+error: implementation of `TheTrait` is not general enough
+  --> $DIR/associated-types-eq-hr.rs:105:5
    |
 LL |     tuple_four::<Tuple>();
-   |     ^^^^^^^^^^^^^^^^^^^ the trait `for<'x, 'y> TheTrait<(&'x isize, &'y isize)>` is not implemented for `Tuple`
-   |
-   = help: the following implementations were found:
-             <Tuple as TheTrait<(&'a isize, &'a isize)>>
-note: required by `tuple_four`
-  --> $DIR/associated-types-eq-hr.rs:74:1
+   |     ^^^^^^^^^^^^^^^^^^^
    |
-LL | / fn tuple_four<T>()
-LL | |     where T : for<'x,'y> TheTrait<(&'x isize, &'y isize)>
-LL | | {
-LL | |     // not ok for tuple, two lifetimes, and lifetime matching is invariant
-LL | | }
-   | |_^
+   = note: Due to a where-clause on `tuple_four`,
+   = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>` for any two lifetimes `'0` and `'1`
+   = note: but `Tuple` only implements `TheTrait<(&'2 isize, &'2 isize)>` for some lifetime `'2`
 
-error: aborting due to 7 previous errors
+error: aborting due to 5 previous errors
 
-Some errors occurred: E0271, E0277.
-For more information about an error, try `rustc --explain E0271`.
+For more information about this error, try `rustc --explain E0271`.
diff --git a/src/test/ui/associated-types/higher-ranked-projection.bad.stderr b/src/test/ui/associated-types/higher-ranked-projection.bad.stderr
index cc69e849fe1..e4704494e14 100644
--- a/src/test/ui/associated-types/higher-ranked-projection.bad.stderr
+++ b/src/test/ui/associated-types/higher-ranked-projection.bad.stderr
@@ -1,17 +1,12 @@
-error[E0271]: type mismatch resolving `for<'a> <&'a _ as Mirror>::Image == _`
+error[E0308]: mismatched types
   --> $DIR/higher-ranked-projection.rs:25:5
    |
 LL |     foo(());
-   |     ^^^ expected bound lifetime parameter 'a, found concrete lifetime
+   |     ^^^ one type is more general than the other
    |
-note: required by `foo`
-  --> $DIR/higher-ranked-projection.rs:14:1
-   |
-LL | / fn foo<U, T>(_t: T)
-LL | |     where for<'a> &'a T: Mirror<Image=U>
-LL | | {}
-   | |__^
+   = note: expected type `Mirror`
+              found type `Mirror`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0271`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/associated-types/higher-ranked-projection.good.stderr b/src/test/ui/associated-types/higher-ranked-projection.good.stderr
index 632fe493e1f..db15ec51d87 100644
--- a/src/test/ui/associated-types/higher-ranked-projection.good.stderr
+++ b/src/test/ui/associated-types/higher-ranked-projection.good.stderr
@@ -3,8 +3,7 @@ error: compilation successful
    |
 LL | / fn main() { //[good]~ ERROR compilation successful
 LL | |     foo(());
-LL | |     //[bad]~^ ERROR type mismatch resolving `for<'a> <&'a _ as Mirror>::Image == _`
-LL | |     //[bad]~| expected bound lifetime parameter 'a, found concrete lifetime
+LL | |     //[bad]~^ ERROR E0308
 LL | | }
    | |_^
 
diff --git a/src/test/ui/associated-types/higher-ranked-projection.rs b/src/test/ui/associated-types/higher-ranked-projection.rs
index be6300c4182..5b380c982f0 100644
--- a/src/test/ui/associated-types/higher-ranked-projection.rs
+++ b/src/test/ui/associated-types/higher-ranked-projection.rs
@@ -23,6 +23,5 @@ fn foo<U, T>(_t: T)
 #[rustc_error]
 fn main() { //[good]~ ERROR compilation successful
     foo(());
-    //[bad]~^ ERROR type mismatch resolving `for<'a> <&'a _ as Mirror>::Image == _`
-    //[bad]~| expected bound lifetime parameter 'a, found concrete lifetime
+    //[bad]~^ ERROR E0308
 }
diff --git a/src/test/ui/closure-expected-type/expect-fn-supply-fn.rs b/src/test/ui/closure-expected-type/expect-fn-supply-fn.rs
index 8c7d3cbad33..6977fd47a2e 100644
--- a/src/test/ui/closure-expected-type/expect-fn-supply-fn.rs
+++ b/src/test/ui/closure-expected-type/expect-fn-supply-fn.rs
@@ -28,14 +28,14 @@ fn expect_free_supply_bound() {
     // Here, we are given a function whose region is bound at closure level,
     // but we expect one bound in the argument. Error results.
     with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
-    //~^ ERROR type mismatch in closure arguments
+    //~^ ERROR mismatched types
 }
 
 fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) {
     // Here, we are given a `fn(&u32)` but we expect a `fn(&'x
     // u32)`. In principle, this could be ok, but we demand equality.
     with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
-    //~^ ERROR type mismatch in closure arguments
+    //~^ ERROR mismatched types
 }
 
 fn expect_bound_supply_free_from_closure() {
@@ -43,8 +43,9 @@ fn expect_bound_supply_free_from_closure() {
     // bound at the closure level, but we expect something bound at
     // the argument level.
     type Foo<'a> = fn(&'a u32);
-    with_closure_expecting_fn_with_bound_region(|_x: Foo<'_>, y| {});
-    //~^ ERROR type mismatch in closure arguments
+    with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| {
+    //~^ ERROR mismatched types
+    });
 }
 
 fn expect_bound_supply_bound<'x>(x: &'x u32) {
diff --git a/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr b/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr
index 9c857431067..b1cfd6cef10 100644
--- a/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr
+++ b/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr
@@ -36,58 +36,33 @@ note: ...does not necessarily outlive the anonymous lifetime #2 defined on the b
 LL |     with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {});
    |                                                ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0631]: type mismatch in closure arguments
-  --> $DIR/expect-fn-supply-fn.rs:30:5
+error[E0308]: mismatched types
+  --> $DIR/expect-fn-supply-fn.rs:30:52
    |
 LL |     with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---------------- found signature of `fn(for<'r> fn(&'r u32), _) -> _`
-   |     |
-   |     expected signature of `for<'a, 'r> fn(fn(&'a u32), &'r i32) -> _`
-   |
-note: required by `with_closure_expecting_fn_with_free_region`
-  --> $DIR/expect-fn-supply-fn.rs:1:1
+   |                                                    ^^^^^^^^ one type is more general than the other
    |
-LL | / fn with_closure_expecting_fn_with_free_region<F>(_: F)
-LL | |     where F: for<'a> FnOnce(fn(&'a u32), &i32)
-LL | | {
-LL | | }
-   | |_^
+   = note: expected type `fn(&u32)`
+              found type `for<'r> fn(&'r u32)`
 
-error[E0631]: type mismatch in closure arguments
-  --> $DIR/expect-fn-supply-fn.rs:37:5
+error[E0308]: mismatched types
+  --> $DIR/expect-fn-supply-fn.rs:37:53
    |
 LL |     with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------------------- found signature of `fn(fn(&'x u32), _) -> _`
-   |     |
-   |     expected signature of `for<'r> fn(for<'s> fn(&'s u32), &'r i32) -> _`
+   |                                                     ^^^^^^^^^^^ one type is more general than the other
    |
-note: required by `with_closure_expecting_fn_with_bound_region`
-  --> $DIR/expect-fn-supply-fn.rs:6:1
-   |
-LL | / fn with_closure_expecting_fn_with_bound_region<F>(_: F)
-LL | |     where F: FnOnce(fn(&u32), &i32)
-LL | | {
-LL | | }
-   | |_^
+   = note: expected type `for<'r> fn(&'r u32)`
+              found type `fn(&'x u32)`
 
-error[E0631]: type mismatch in closure arguments
-  --> $DIR/expect-fn-supply-fn.rs:46:5
-   |
-LL |     with_closure_expecting_fn_with_bound_region(|_x: Foo<'_>, y| {});
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---------------- found signature of `for<'r> fn(fn(&'r u32), _) -> _`
-   |     |
-   |     expected signature of `for<'r> fn(for<'s> fn(&'s u32), &'r i32) -> _`
+error[E0308]: mismatched types
+  --> $DIR/expect-fn-supply-fn.rs:46:53
    |
-note: required by `with_closure_expecting_fn_with_bound_region`
-  --> $DIR/expect-fn-supply-fn.rs:6:1
+LL |     with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| {
+   |                                                     ^^^^^^^ one type is more general than the other
    |
-LL | / fn with_closure_expecting_fn_with_bound_region<F>(_: F)
-LL | |     where F: FnOnce(fn(&u32), &i32)
-LL | | {
-LL | | }
-   | |_^
+   = note: expected type `for<'r> fn(&'r u32)`
+              found type `fn(&u32)`
 
 error: aborting due to 5 previous errors
 
-Some errors occurred: E0308, E0631.
-For more information about an error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/coherence/coherence-subtyping.rs b/src/test/ui/coherence/coherence-subtyping.rs
new file mode 100644
index 00000000000..fb9a7fbf7ab
--- /dev/null
+++ b/src/test/ui/coherence/coherence-subtyping.rs
@@ -0,0 +1,15 @@
+// Test that two distinct impls which match subtypes of one another
+// yield coherence errors (or not) depending on the variance.
+
+trait TheTrait {
+    fn foo(&self) { }
+}
+
+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 {
+    //~^ ERROR
+}
+
+fn main() { }
diff --git a/src/test/ui/coherence/coherence-subtyping.stderr b/src/test/ui/coherence/coherence-subtyping.stderr
new file mode 100644
index 00000000000..1fc5c39d5c9
--- /dev/null
+++ b/src/test/ui/coherence/coherence-subtyping.stderr
@@ -0,0 +1,14 @@
+error[E0119]: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`:
+  --> $DIR/coherence-subtyping.rs:11:1
+   |
+LL | impl TheTrait for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
+   | --------------------------------------------------------- first implementation here
+...
+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: 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 E0119`.
diff --git a/src/test/ui/generator/auto-trait-regions.rs b/src/test/ui/generator/auto-trait-regions.rs
index 2110e8a5407..46d72899438 100644
--- a/src/test/ui/generator/auto-trait-regions.rs
+++ b/src/test/ui/generator/auto-trait-regions.rs
@@ -27,7 +27,8 @@ fn main() {
         yield;
         assert_foo(x);
     };
-    assert_foo(gen); //~ ERROR the trait bound `No: Foo` is not satisfied
+    assert_foo(gen);
+    //~^ ERROR implementation of `Foo` is not general enough
 
     // Allow impls which matches any lifetime
     let x = &OnlyFooIfRef(No);
@@ -44,5 +45,6 @@ fn main() {
         yield;
         assert_foo(a);
     };
-    assert_foo(gen); //~ ERROR the requirement `for<'r, 's> 'r : 's` is not satisfied
+    assert_foo(gen);
+    //~^ ERROR not general enough
 }
diff --git a/src/test/ui/generator/auto-trait-regions.stderr b/src/test/ui/generator/auto-trait-regions.stderr
index a301dc2ff9a..1b4dfe2df1c 100644
--- a/src/test/ui/generator/auto-trait-regions.stderr
+++ b/src/test/ui/generator/auto-trait-regions.stderr
@@ -1,37 +1,20 @@
-error[E0277]: the trait bound `No: Foo` is not satisfied in `[generator@$DIR/auto-trait-regions.rs:25:15: 29:6 x:&&OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]`
+error: implementation of `Foo` is not general enough
   --> $DIR/auto-trait-regions.rs:30:5
    |
-LL |     assert_foo(gen); //~ ERROR the trait bound `No: Foo` is not satisfied
-   |     ^^^^^^^^^^ within `[generator@$DIR/auto-trait-regions.rs:25:15: 29:6 x:&&OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]`, the trait `Foo` is not implemented for `No`
-   |
-   = help: the following implementations were found:
-             <No as Foo>
-   = note: required because it appears within the type `OnlyFooIfStaticRef`
-   = note: required because it appears within the type `&OnlyFooIfStaticRef`
-   = note: required because it appears within the type `for<'r> {&'r OnlyFooIfStaticRef, ()}`
-   = note: required because it appears within the type `[generator@$DIR/auto-trait-regions.rs:25:15: 29:6 x:&&OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]`
-note: required by `assert_foo`
-  --> $DIR/auto-trait-regions.rs:20:1
+LL |     assert_foo(gen);
+   |     ^^^^^^^^^^
    |
-LL | fn assert_foo<T: Foo>(f: T) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `&'0 OnlyFooIfStaticRef` must implement `Foo` for any lifetime `'0`
+   = note: but `&'1 OnlyFooIfStaticRef` only implements `Foo` for the lifetime `'1`
 
-error[E0279]: the requirement `for<'r, 's> 'r : 's` is not satisfied (`expected bound lifetime parameter, found concrete lifetime`)
-  --> $DIR/auto-trait-regions.rs:47:5
+error: implementation of `Foo` is not general enough
+  --> $DIR/auto-trait-regions.rs:48:5
    |
-LL |     assert_foo(gen); //~ ERROR the requirement `for<'r, 's> 'r : 's` is not satisfied
+LL |     assert_foo(gen);
    |     ^^^^^^^^^^
    |
-   = note: required because of the requirements on the impl of `for<'r, 's> Foo` for `A<'_, '_>`
-   = note: required because it appears within the type `for<'r, 's> {A<'r, 's>, ()}`
-   = note: required because it appears within the type `[generator@$DIR/auto-trait-regions.rs:42:15: 46:6 for<'r, 's> {A<'r, 's>, ()}]`
-note: required by `assert_foo`
-  --> $DIR/auto-trait-regions.rs:20:1
-   |
-LL | fn assert_foo<T: Foo>(f: T) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `A<'0, '1>` must implement `Foo` for any two lifetimes `'0` and `'1`
+   = note: but `A<'_, '2>` only implements `Foo` for the lifetime `'2`
 
 error: aborting due to 2 previous errors
 
-Some errors occurred: E0277, E0279.
-For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr
index 8e2b0b8c600..bdfabdabbeb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/hr-subtype.rs:39:26
    |
 LL |               gimme::<$t1>(None::<$t2>);
-   |                            ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
+   |                            ^^^^^^^^^^^ one type is more general than the other
 ...
 LL | / check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32,
 LL | |                                             for<'a>    fn(&'a u32, &'a u32) -> &'a u32) }
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr
index dbb50181390..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr
@@ -1,16 +1,14 @@
-error[E0308]: mismatched types
-  --> $DIR/hr-subtype.rs:39:26
+error: compilation successful
+  --> $DIR/hr-subtype.rs:96:1
    |
-LL |               gimme::<$t1>(None::<$t2>);
-   |                            ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
-...
-LL | / check! { bound_a_b_vs_bound_a: (for<'a,'b> fn(&'a u32, &'b u32),
-LL | |                                 for<'a>    fn(&'a u32, &'a u32)) }
-   | |__________________________________________________________________- in this macro invocation
-   |
-   = note: expected type `std::option::Option<for<'a, 'b> fn(&'a u32, &'b u32)>`
-              found type `std::option::Option<for<'a> fn(&'a u32, &'a u32)>`
+LL | / fn main() {
+LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
+LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
+LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
+LL | | }
+   | |_^
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr
index 5fcb63e17bf..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr
@@ -1,12 +1,12 @@
 error: compilation successful
-  --> $DIR/hr-subtype.rs:100:1
+  --> $DIR/hr-subtype.rs:96:1
    |
 LL | / fn main() {
 LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
 LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
 LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
-LL | | //[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
-LL | | //[free_x_vs_free_x]~^^^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
 LL | | }
    | |_^
 
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr
index 5fcb63e17bf..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr
@@ -1,12 +1,12 @@
 error: compilation successful
-  --> $DIR/hr-subtype.rs:100:1
+  --> $DIR/hr-subtype.rs:96:1
    |
 LL | / fn main() {
 LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
 LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
 LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
-LL | | //[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
-LL | | //[free_x_vs_free_x]~^^^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
 LL | | }
    | |_^
 
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr
index db9892b48a6..74b8c89b6e8 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/hr-subtype.rs:39:26
    |
 LL |               gimme::<$t1>(None::<$t2>);
-   |                            ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
+   |                            ^^^^^^^^^^^ one type is more general than the other
 ...
 LL | / check! { bound_a_vs_free_x: (for<'a> fn(&'a u32),
 LL | |                              fn(&'x u32)) }
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr
index e9fb73411bd..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr
@@ -1,16 +1,14 @@
-error[E0308]: mismatched types
-  --> $DIR/hr-subtype.rs:39:26
+error: compilation successful
+  --> $DIR/hr-subtype.rs:96:1
    |
-LL |               gimme::<$t1>(None::<$t2>);
-   |                            ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
-...
-LL | / check! { bound_co_a_b_vs_bound_co_a: (for<'a,'b> fn(Co<'a>, Co<'b>),
-LL | |                                       for<'a>    fn(Co<'a>, Co<'a>)) }
-   | |______________________________________________________________________- in this macro invocation
-   |
-   = note: expected type `std::option::Option<for<'a, 'b> fn(Co<'a>, Co<'b>)>`
-              found type `std::option::Option<for<'a> fn(Co<'a>, Co<'a>)>`
+LL | / fn main() {
+LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
+LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
+LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
+LL | | }
+   | |_^
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr
index d0e80faa68e..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr
@@ -1,16 +1,14 @@
-error[E0308]: mismatched types
-  --> $DIR/hr-subtype.rs:39:26
+error: compilation successful
+  --> $DIR/hr-subtype.rs:96:1
    |
-LL |               gimme::<$t1>(None::<$t2>);
-   |                            ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
-...
-LL | / check! { bound_co_a_co_b_ret_contra_a: (for<'a,'b> fn(Co<'a>, Co<'b>) -> Contra<'a>,
-LL | |                                         for<'a>    fn(Co<'a>, Co<'a>) -> Contra<'a>) }
-   | |______________________________________________________________________________________- in this macro invocation
-   |
-   = note: expected type `std::option::Option<for<'a, 'b> fn(Co<'a>, Co<'b>) -> Contra<'a>>`
-              found type `std::option::Option<for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>>`
+LL | / fn main() {
+LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
+LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
+LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
+LL | | }
+   | |_^
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr
index 5fcb63e17bf..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr
@@ -1,12 +1,12 @@
 error: compilation successful
-  --> $DIR/hr-subtype.rs:100:1
+  --> $DIR/hr-subtype.rs:96:1
    |
 LL | / fn main() {
 LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
 LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
 LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
-LL | | //[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
-LL | | //[free_x_vs_free_x]~^^^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
 LL | | }
    | |_^
 
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr
index 3605ecf4f86..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr
@@ -1,16 +1,14 @@
-error[E0308]: mismatched types
-  --> $DIR/hr-subtype.rs:39:26
+error: compilation successful
+  --> $DIR/hr-subtype.rs:96:1
    |
-LL |               gimme::<$t1>(None::<$t2>);
-   |                            ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
-...
-LL | / check! { bound_contra_a_contra_b_ret_co_a: (for<'a,'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>,
-LL | |                                             for<'a>    fn(Contra<'a>, Contra<'a>) -> Co<'a>) }
-   | |______________________________________________________________________________________________- in this macro invocation
-   |
-   = note: expected type `std::option::Option<for<'a, 'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>>`
-              found type `std::option::Option<for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>>`
+LL | / fn main() {
+LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
+LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
+LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
+LL | | }
+   | |_^
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr
index fae6e9b5c89..8168941e277 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/hr-subtype.rs:39:26
    |
 LL |               gimme::<$t1>(None::<$t2>);
-   |                            ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
+   |                            ^^^^^^^^^^^ one type is more general than the other
 ...
 LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
 LL | |                                         for<'a>    fn(Inv<'a>, Inv<'a>)) }
diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr
index 5fcb63e17bf..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr
@@ -1,12 +1,12 @@
 error: compilation successful
-  --> $DIR/hr-subtype.rs:100:1
+  --> $DIR/hr-subtype.rs:96:1
    |
 LL | / fn main() {
 LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
 LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
 LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
-LL | | //[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
-LL | | //[free_x_vs_free_x]~^^^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
 LL | | }
    | |_^
 
diff --git a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr
index 5fcb63e17bf..25b74d855bb 100644
--- a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr
+++ b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr
@@ -1,12 +1,12 @@
 error: compilation successful
-  --> $DIR/hr-subtype.rs:100:1
+  --> $DIR/hr-subtype.rs:96:1
    |
 LL | / fn main() {
 LL | | //[bound_a_vs_bound_a]~^ ERROR compilation successful
 LL | | //[bound_a_vs_bound_b]~^^ ERROR compilation successful
 LL | | //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
-LL | | //[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
-LL | | //[free_x_vs_free_x]~^^^^^ ERROR compilation successful
+...  |
+LL | | //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
 LL | | }
    | |_^
 
diff --git a/src/test/ui/hr-subtype/hr-subtype.rs b/src/test/ui/hr-subtype/hr-subtype.rs
index 4157953fb90..ad4f39f8405 100644
--- a/src/test/ui/hr-subtype/hr-subtype.rs
+++ b/src/test/ui/hr-subtype/hr-subtype.rs
@@ -31,21 +31,17 @@ macro_rules! check {
         #[cfg($rev)]
         fn subtype<'x,'y:'x,'z:'y>() {
             gimme::<$t2>(None::<$t1>);
-            //[free_inv_x_vs_free_inv_y]~^ ERROR mismatched types
+            //[free_inv_x_vs_free_inv_y]~^ ERROR
         }
 
         #[cfg($rev)]
         fn supertype<'x,'y:'x,'z:'y>() {
             gimme::<$t1>(None::<$t2>);
-            //[bound_a_vs_free_x]~^ ERROR mismatched types
-            //[free_x_vs_free_y]~^^ ERROR mismatched types
-            //[bound_inv_a_b_vs_bound_inv_a]~^^^ ERROR mismatched types
-            //[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^ ERROR mismatched types
-            //[free_inv_x_vs_free_inv_y]~^^^^^ ERROR mismatched types
-            //[bound_a_b_vs_bound_a]~^^^^^^ ERROR mismatched types
-            //[bound_co_a_b_vs_bound_co_a]~^^^^^^^ ERROR mismatched types
-            //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^ ERROR mismatched types
-            //[bound_co_a_co_b_ret_contra_a]~^^^^^^^^^ ERROR mismatched types
+            //[bound_a_vs_free_x]~^ ERROR
+            //[free_x_vs_free_y]~^^ ERROR
+            //[bound_inv_a_b_vs_bound_inv_a]~^^^ ERROR
+            //[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^ ERROR
+            //[free_inv_x_vs_free_inv_y]~^^^^^ ERROR
         }
     }
 }
@@ -103,4 +99,8 @@ fn main() {
 //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
 //[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
 //[free_x_vs_free_x]~^^^^^ ERROR compilation successful
+//[bound_a_b_vs_bound_a]~^^^^^^ ERROR compilation successful
+//[bound_co_a_co_b_ret_contra_a]~^^^^^^^ ERROR compilation successful
+//[bound_co_a_b_vs_bound_co_a]~^^^^^^^^ ERROR compilation successful
+//[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR compilation successful
 }
diff --git a/src/test/ui/hrtb/hrtb-cache-issue-54302.rs b/src/test/ui/hrtb/hrtb-cache-issue-54302.rs
new file mode 100644
index 00000000000..a20d03c7747
--- /dev/null
+++ b/src/test/ui/hrtb/hrtb-cache-issue-54302.rs
@@ -0,0 +1,24 @@
+// Regression test for #54302.
+//
+// We were incorrectly using the "evaluation cache" (which ignored
+// region results) to conclude that `&'static str: Deserialize`, even
+// though it would require that `for<'de> 'de: 'static`, which is
+// clearly false.
+
+trait Deserialize<'de> {}
+
+trait DeserializeOwned: for<'de> Deserialize<'de> {}
+impl<T> DeserializeOwned for T where T: for<'de> Deserialize<'de> {}
+
+// Based on this impl, `&'static str` only implements Deserialize<'static>.
+// It does not implement for<'de> Deserialize<'de>.
+impl<'de: 'a, 'a> Deserialize<'de> for &'a str {}
+
+fn main() {
+    fn assert_deserialize_owned<T: DeserializeOwned>() {}
+    assert_deserialize_owned::<&'static str>(); //~ ERROR
+
+    // It correctly does not implement for<'de> Deserialize<'de>.
+    // fn assert_hrtb<T: for<'de> Deserialize<'de>>() {}
+    // assert_hrtb::<&'static str>();
+}
diff --git a/src/test/ui/hrtb/hrtb-cache-issue-54302.stderr b/src/test/ui/hrtb/hrtb-cache-issue-54302.stderr
new file mode 100644
index 00000000000..e82fa51524e
--- /dev/null
+++ b/src/test/ui/hrtb/hrtb-cache-issue-54302.stderr
@@ -0,0 +1,11 @@
+error: implementation of `Deserialize` is not general enough
+  --> $DIR/hrtb-cache-issue-54302.rs:19:5
+   |
+LL |     assert_deserialize_owned::<&'static str>(); //~ ERROR
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `&'static str` must implement `Deserialize<'0>` for any lifetime `'0`
+   = note: but `&str` only implements `Deserialize<'1>` for some lifetime `'1`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/hrtb/hrtb-conflate-regions.rs b/src/test/ui/hrtb/hrtb-conflate-regions.rs
index 8953a847213..391303676d7 100644
--- a/src/test/ui/hrtb/hrtb-conflate-regions.rs
+++ b/src/test/ui/hrtb/hrtb-conflate-regions.rs
@@ -25,6 +25,6 @@ impl<'a> Foo<(&'a isize, &'a isize)> for SomeStruct
 }
 
 fn a() { want_foo1::<SomeStruct>(); } // OK -- foo wants just one region
-fn b() { want_foo2::<SomeStruct>(); } //~ ERROR E0277
+fn b() { want_foo2::<SomeStruct>(); } //~ ERROR
 
 fn main() { }
diff --git a/src/test/ui/hrtb/hrtb-conflate-regions.stderr b/src/test/ui/hrtb/hrtb-conflate-regions.stderr
index e2a99ea8472..2ee398e3dd3 100644
--- a/src/test/ui/hrtb/hrtb-conflate-regions.stderr
+++ b/src/test/ui/hrtb/hrtb-conflate-regions.stderr
@@ -1,20 +1,12 @@
-error[E0277]: the trait bound `for<'a, 'b> SomeStruct: Foo<(&'a isize, &'b isize)>` is not satisfied
+error: implementation of `Foo` is not general enough
   --> $DIR/hrtb-conflate-regions.rs:28:10
    |
-LL | fn b() { want_foo2::<SomeStruct>(); } //~ ERROR E0277
-   |          ^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a, 'b> Foo<(&'a isize, &'b isize)>` is not implemented for `SomeStruct`
+LL | fn b() { want_foo2::<SomeStruct>(); } //~ ERROR
+   |          ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: the following implementations were found:
-             <SomeStruct as Foo<(&'a isize, &'a isize)>>
-note: required by `want_foo2`
-  --> $DIR/hrtb-conflate-regions.rs:8:1
-   |
-LL | / fn want_foo2<T>()
-LL | |     where T : for<'a,'b> Foo<(&'a isize, &'b isize)>
-LL | | {
-LL | | }
-   | |_^
+   = note: Due to a where-clause on `want_foo2`,
+   = note: `SomeStruct` must implement `Foo<(&'0 isize, &'1 isize)>` for any two lifetimes `'0` and `'1`
+   = note: but `SomeStruct` only implements `Foo<(&'2 isize, &'2 isize)>` for some lifetime `'2`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/hrtb/hrtb-exists-forall-fn.rs b/src/test/ui/hrtb/hrtb-exists-forall-fn.rs
new file mode 100644
index 00000000000..828331cb950
--- /dev/null
+++ b/src/test/ui/hrtb/hrtb-exists-forall-fn.rs
@@ -0,0 +1,18 @@
+// Test a `exists<'a> { forall<'b> { 'a = 'b } }` pattern -- which should not compile!
+//
+// In particular, we test this pattern in trait solving, where it is not connected
+// to any part of the source code.
+
+fn foo<'a>() -> fn(&'a u32) {
+    panic!()
+}
+
+fn main() {
+    // Here, proving that `fn(&'a u32) <: for<'b> fn(&'b u32)`:
+    //
+    // - instantiates `'b` with a placeholder `!b`,
+    // - requires that `&!b u32 <: &'a u32` and hence that `!b: 'a`,
+    // - but we can never know this.
+
+    let _: for<'b> fn(&'b u32) = foo(); //~ ERROR mismatched types
+}
diff --git a/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr b/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr
new file mode 100644
index 00000000000..6301ed45ac2
--- /dev/null
+++ b/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr
@@ -0,0 +1,12 @@
+error[E0308]: mismatched types
+  --> $DIR/hrtb-exists-forall-fn.rs:17:34
+   |
+LL |     let _: for<'b> fn(&'b u32) = foo(); //~ ERROR mismatched types
+   |                                  ^^^^^ one type is more general than the other
+   |
+   = note: expected type `for<'b> fn(&'b u32)`
+              found type `fn(&u32)`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.rs b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.rs
new file mode 100644
index 00000000000..8801760056e
--- /dev/null
+++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.rs
@@ -0,0 +1,35 @@
+// Test a `exists<'a> { forall<'b> { 'a = 'b } }` pattern -- which should not compile!
+//
+// In particular, we test this pattern in trait solving, where it is not connected
+// to any part of the source code.
+//
+// compile-pass
+
+trait Trait<T> {}
+
+fn foo<T>()
+where
+    T: Trait<for<'b> fn(&'b u32)>,
+{
+}
+
+impl<'a> Trait<fn(&'a u32)> for () {}
+
+fn main() {
+    // Here, proving that `(): Trait<for<'b> fn(&'b u32)>` uses the impl:
+    //
+    // - The impl provides the clause `forall<'a> { (): Trait<fn(&'a u32)> }`
+    // - We instantiate `'a` existentially to get `(): Trait<fn(&?a u32)>`
+    // - We unify `fn(&?a u32)` with `for<'b> fn(&'b u32)` -- this does a
+    //   "bidirectional" subtyping check, so we wind up with:
+    //   - `fn(&?a u32) <: for<'b> fn(&'b u32)` :-
+    //     - `&'!b u32 <: &?a u32`
+    //     - `!'b: ?a` -- solveable if `?a` is inferred to `'empty`
+    //   - `for<'b> fn(&'b u32) <: fn(&?a u32)` :-
+    //     - `&?a u32 u32 <: &?b u32`
+    //     - `?a: ?b` -- solveable if `?b` is also inferred to `'empty`
+    // - So the subtyping check succeeds, somewhat surprisingly.
+    //   This is because we can use `'empty`.
+
+    foo::<()>();
+}
diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.rs b/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.rs
new file mode 100644
index 00000000000..da1bb7cd5fd
--- /dev/null
+++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.rs
@@ -0,0 +1,37 @@
+// Test a `exists<'a> { forall<'b> { 'a = 'b } }` pattern -- which should not compile!
+//
+// In particular, we test this pattern in trait solving, where it is not connected
+// to any part of the source code.
+//
+// compile-pass
+
+trait Trait<T> {}
+
+fn foo<T>()
+where
+    T: Trait<for<'b> fn(fn(&'b u32))>,
+{
+}
+
+impl<'a> Trait<fn(fn(&'a u32))> for () {}
+
+fn main() {
+    // Here, proving that `(): Trait<for<'b> fn(&'b u32)>` uses the impl:
+    //
+    // - The impl provides the clause `forall<'a> { (): Trait<fn(fn(&'a u32))> }`
+    // - We instantiate `'a` existentially to get `(): Trait<fn(fn(&?a u32))>`
+    // - We unify `fn(fn(&?a u32))` with `for<'b> fn(fn(&'b u32))` -- this does a
+    //   "bidirectional" subtyping check, so we wind up with:
+    //   - `fn(fn(&?a u32)) <: for<'b> fn(fn(&'b u32))` :-
+    //     - `fn(&!b u32) <: fn(&?a u32)`
+    //       - `&?a u32 <: &!b u32`
+    //         - `?a: !'b` -- solveable if `?a` is inferred to `'static`
+    //   - `for<'b> fn(fn(&'b u32)) <: fn(fn(&?a u32))` :-
+    //     - `fn(&?a u32) <: fn(&?b u32)`
+    //       - `&?b u32 <: &?a u32`
+    //         - `?b: ?a` -- solveable if `?b` is inferred to `'static`
+    // - So the subtyping check succeeds, somewhat surprisingly.
+    //   This is because we can use `'static`.
+
+    foo::<()>();
+}
diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.rs b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.rs
new file mode 100644
index 00000000000..da3f8ad1b89
--- /dev/null
+++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.rs
@@ -0,0 +1,29 @@
+// Test a `exists<'a> { forall<'b> { 'a = 'b } }` pattern -- which should not compile!
+//
+// In particular, we test this pattern in trait solving, where it is not connected
+// to any part of the source code.
+
+use std::cell::Cell;
+
+trait Trait<T> {}
+
+fn foo<T>()
+where
+    T: Trait<for<'b> fn(Cell<&'b u32>)>,
+{
+}
+
+impl<'a> Trait<fn(Cell<&'a u32>)> for () {}
+
+fn main() {
+    // Here, proving that `(): Trait<for<'b> fn(&'b u32)>` uses the impl:
+    //
+    // - The impl provides the clause `forall<'a> { (): Trait<fn(&'a u32)> }`
+    // - We instantiate `'a` existentially to get `(): Trait<fn(&?a u32)>`
+    // - We unify `fn(&?a u32)` with `for<'b> fn(&'b u32)`
+    //   - This requires (among other things) instantiating `'b` universally,
+    //     yielding `fn(&!b u32)`, in a fresh universe U1
+    //   - So we get `?a = !b` but the universe U0 assigned to `?a` cannot name `!b`.
+
+    foo::<()>(); //~ ERROR not general enough
+}
diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr
new file mode 100644
index 00000000000..6a61181e240
--- /dev/null
+++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr
@@ -0,0 +1,12 @@
+error: implementation of `Trait` is not general enough
+  --> $DIR/hrtb-exists-forall-trait-invariant.rs:28:5
+   |
+LL |     foo::<()>(); //~ ERROR not general enough
+   |     ^^^^^^^^^
+   |
+   = note: Due to a where-clause on `foo`,
+   = note: `()` must implement `Trait<for<'b> fn(std::cell::Cell<&'b u32>)>`
+   = note: but `()` only implements `Trait<fn(std::cell::Cell<&'0 u32>)>` for some lifetime `'0`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.rs b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.rs
index 99001d7053c..f9ae1429ee4 100644
--- a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.rs
+++ b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.rs
@@ -44,7 +44,7 @@ fn want_qux<B>(b: &B)
     where B : Qux
 {
     want_foo_for_any_tcx(b);
-    want_bar_for_any_ccx(b); //~ ERROR E0277
+    want_bar_for_any_ccx(b); //~ ERROR
 }
 
 fn main() {}
diff --git a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr
index d233ed47524..0d7b5cbf823 100644
--- a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr
+++ b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits-transitive.stderr
@@ -1,19 +1,12 @@
-error[E0277]: the trait bound `for<'ccx> B: Bar<'ccx>` is not satisfied
+error[E0308]: mismatched types
   --> $DIR/hrtb-higher-ranker-supertraits-transitive.rs:47:5
    |
-LL |     want_bar_for_any_ccx(b); //~ ERROR E0277
-   |     ^^^^^^^^^^^^^^^^^^^^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B`
+LL |     want_bar_for_any_ccx(b); //~ ERROR
+   |     ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
    |
-   = help: consider adding a `where for<'ccx> B: Bar<'ccx>` bound
-note: required by `want_bar_for_any_ccx`
-  --> $DIR/hrtb-higher-ranker-supertraits-transitive.rs:31:1
-   |
-LL | / fn want_bar_for_any_ccx<B>(b: &B)
-LL | |     where B : for<'ccx> Bar<'ccx>
-LL | | {
-LL | | }
-   | |_^
+   = note: expected type `for<'ccx> Bar<'ccx>`
+              found type `Bar<'static>`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.rs b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.rs
index f430a10d2a7..3d2d403462d 100644
--- a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.rs
+++ b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.rs
@@ -15,7 +15,7 @@ fn want_foo_for_some_tcx<'x,F>(f: &'x F)
     where F : Foo<'x>
 {
     want_foo_for_some_tcx(f);
-    want_foo_for_any_tcx(f); //~ ERROR E0277
+    want_foo_for_any_tcx(f); //~ ERROR E0308
 }
 
 fn want_foo_for_any_tcx<F>(f: &F)
@@ -32,7 +32,7 @@ fn want_bar_for_some_ccx<'x,B>(b: &B)
     want_foo_for_any_tcx(b);
 
     want_bar_for_some_ccx(b);
-    want_bar_for_any_ccx(b); //~ ERROR E0277
+    want_bar_for_any_ccx(b); //~ ERROR E0308
 }
 
 fn want_bar_for_any_ccx<B>(b: &B)
diff --git a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr
index fe485c5b259..31dbeec2a55 100644
--- a/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr
+++ b/src/test/ui/hrtb/hrtb-higher-ranker-supertraits.stderr
@@ -1,40 +1,21 @@
-error[E0277]: the trait bound `for<'tcx> F: Foo<'tcx>` is not satisfied
+error[E0308]: mismatched types
   --> $DIR/hrtb-higher-ranker-supertraits.rs:18:5
    |
-LL |     want_foo_for_any_tcx(f); //~ ERROR E0277
-   |     ^^^^^^^^^^^^^^^^^^^^ the trait `for<'tcx> Foo<'tcx>` is not implemented for `F`
+LL |     want_foo_for_any_tcx(f); //~ ERROR E0308
+   |     ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
    |
-   = help: consider adding a `where for<'tcx> F: Foo<'tcx>` bound
-note: required by `want_foo_for_any_tcx`
-  --> $DIR/hrtb-higher-ranker-supertraits.rs:21:1
-   |
-LL | / fn want_foo_for_any_tcx<F>(f: &F)
-LL | |     where F : for<'tcx> Foo<'tcx>
-LL | | {
-LL | |     want_foo_for_some_tcx(f);
-LL | |     want_foo_for_any_tcx(f);
-LL | | }
-   | |_^
+   = note: expected type `for<'tcx> Foo<'tcx>`
+              found type `Foo<'x>`
 
-error[E0277]: the trait bound `for<'ccx> B: Bar<'ccx>` is not satisfied
+error[E0308]: mismatched types
   --> $DIR/hrtb-higher-ranker-supertraits.rs:35:5
    |
-LL |     want_bar_for_any_ccx(b); //~ ERROR E0277
-   |     ^^^^^^^^^^^^^^^^^^^^ the trait `for<'ccx> Bar<'ccx>` is not implemented for `B`
-   |
-   = help: consider adding a `where for<'ccx> B: Bar<'ccx>` bound
-note: required by `want_bar_for_any_ccx`
-  --> $DIR/hrtb-higher-ranker-supertraits.rs:38:1
+LL |     want_bar_for_any_ccx(b); //~ ERROR E0308
+   |     ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
    |
-LL | / fn want_bar_for_any_ccx<B>(b: &B)
-LL | |     where B : for<'ccx> Bar<'ccx>
-LL | | {
-LL | |     want_foo_for_some_tcx(b);
-...  |
-LL | |     want_bar_for_any_ccx(b);
-LL | | }
-   | |_^
+   = note: expected type `for<'ccx> Bar<'ccx>`
+              found type `Bar<'x>`
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/hrtb/hrtb-just-for-static.rs b/src/test/ui/hrtb/hrtb-just-for-static.rs
index c162c777c0b..88d5ce8e640 100644
--- a/src/test/ui/hrtb/hrtb-just-for-static.rs
+++ b/src/test/ui/hrtb/hrtb-just-for-static.rs
@@ -21,7 +21,13 @@ fn give_any() {
 struct StaticInt;
 impl Foo<&'static isize> for StaticInt { }
 fn give_static() {
-    want_hrtb::<StaticInt>() //~ ERROR `for<'a> StaticInt: Foo<&'a isize>` is not satisfied
+    want_hrtb::<StaticInt>() //~ ERROR
+}
+
+// AnyInt implements Foo<&'a isize> for any 'a, so it is a match.
+impl<'a> Foo<&'a isize> for &'a u32 { }
+fn give_some<'a>() {
+    want_hrtb::<&'a u32>() //~ ERROR
 }
 
 fn main() { }
diff --git a/src/test/ui/hrtb/hrtb-just-for-static.stderr b/src/test/ui/hrtb/hrtb-just-for-static.stderr
index 38865fac86d..094c4498024 100644
--- a/src/test/ui/hrtb/hrtb-just-for-static.stderr
+++ b/src/test/ui/hrtb/hrtb-just-for-static.stderr
@@ -1,20 +1,22 @@
-error[E0277]: the trait bound `for<'a> StaticInt: Foo<&'a isize>` is not satisfied
+error: implementation of `Foo` is not general enough
   --> $DIR/hrtb-just-for-static.rs:24:5
    |
-LL |     want_hrtb::<StaticInt>() //~ ERROR `for<'a> StaticInt: Foo<&'a isize>` is not satisfied
-   |     ^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Foo<&'a isize>` is not implemented for `StaticInt`
+LL |     want_hrtb::<StaticInt>() //~ ERROR
+   |     ^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: the following implementations were found:
-             <StaticInt as Foo<&'static isize>>
-note: required by `want_hrtb`
-  --> $DIR/hrtb-just-for-static.rs:8:1
+   = note: Due to a where-clause on `want_hrtb`,
+   = note: `StaticInt` must implement `Foo<&'0 isize>` for any lifetime `'0`
+   = note: but `StaticInt` only implements `Foo<&'1 isize>` for some lifetime `'1`
+
+error: implementation of `Foo` is not general enough
+  --> $DIR/hrtb-just-for-static.rs:30:5
+   |
+LL |     want_hrtb::<&'a u32>() //~ ERROR
+   |     ^^^^^^^^^^^^^^^^^^^^
    |
-LL | / fn want_hrtb<T>()
-LL | |     where T : for<'a> Foo<&'a isize>
-LL | | {
-LL | | }
-   | |_^
+   = note: Due to a where-clause on `want_hrtb`,
+   = note: `&'a u32` must implement `Foo<&'0 isize>` for any lifetime `'0`
+   = note: but `&'1 u32` only implements `Foo<&'1 isize>` for the lifetime `'1`
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/hrtb/hrtb-perfect-forwarding.rs b/src/test/ui/hrtb/hrtb-perfect-forwarding.rs
index 90e1773a3f6..7bd89960e42 100644
--- a/src/test/ui/hrtb/hrtb-perfect-forwarding.rs
+++ b/src/test/ui/hrtb/hrtb-perfect-forwarding.rs
@@ -43,7 +43,7 @@ fn foo_hrtb_bar_not<'b,T>(mut t: T)
     // be implemented. Thus to satisfy `&mut T : for<'a> Foo<&'a
     // isize>`, we require `T : for<'a> Bar<&'a isize>`, but the where
     // clause only specifies `T : Bar<&'b isize>`.
-    foo_hrtb_bar_not(&mut t); //~ ERROR `for<'a> T: Bar<&'a isize>` is not satisfied
+    foo_hrtb_bar_not(&mut t); //~ ERROR E0308
 }
 
 fn foo_hrtb_bar_hrtb<T>(mut t: T)
diff --git a/src/test/ui/hrtb/hrtb-perfect-forwarding.stderr b/src/test/ui/hrtb/hrtb-perfect-forwarding.stderr
index 6cada7ac78f..ec3bf8a1a1b 100644
--- a/src/test/ui/hrtb/hrtb-perfect-forwarding.stderr
+++ b/src/test/ui/hrtb/hrtb-perfect-forwarding.stderr
@@ -1,23 +1,12 @@
-error[E0277]: the trait bound `for<'a> T: Bar<&'a isize>` is not satisfied
+error[E0308]: mismatched types
   --> $DIR/hrtb-perfect-forwarding.rs:46:5
    |
-LL |     foo_hrtb_bar_not(&mut t); //~ ERROR `for<'a> T: Bar<&'a isize>` is not satisfied
-   |     ^^^^^^^^^^^^^^^^ the trait `for<'a> Bar<&'a isize>` is not implemented for `T`
+LL |     foo_hrtb_bar_not(&mut t); //~ ERROR E0308
+   |     ^^^^^^^^^^^^^^^^ one type is more general than the other
    |
-   = help: consider adding a `where for<'a> T: Bar<&'a isize>` bound
-   = note: required because of the requirements on the impl of `for<'a> Foo<&'a isize>` for `&mut T`
-note: required by `foo_hrtb_bar_not`
-  --> $DIR/hrtb-perfect-forwarding.rs:39:1
-   |
-LL | / fn foo_hrtb_bar_not<'b,T>(mut t: T)
-LL | |     where T : for<'a> Foo<&'a isize> + Bar<&'b isize>
-LL | | {
-LL | |     // Not OK -- The forwarding impl for `Foo` requires that `Bar` also
-...  |
-LL | |     foo_hrtb_bar_not(&mut t); //~ ERROR `for<'a> T: Bar<&'a isize>` is not satisfied
-LL | | }
-   | |_^
+   = note: expected type `Foo<&'a isize>`
+              found type `Foo<&isize>`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/issues/issue-22872.rs b/src/test/ui/issues/issue-22872.rs
index a6130d21b5d..8ef4af15bd4 100644
--- a/src/test/ui/issues/issue-22872.rs
+++ b/src/test/ui/issues/issue-22872.rs
@@ -19,7 +19,6 @@ pub trait Process<'a> {
 fn push_process<P>(process: P) where P: Process<'static> {
     let _: Box<for<'b> Wrap<'b>> = Box::new(Wrapper(process));
 //~^ ERROR is not an iterator
-//~| ERROR is not satisfied
 }
 
 fn main() {}
diff --git a/src/test/ui/issues/issue-22872.stderr b/src/test/ui/issues/issue-22872.stderr
index 231080add9b..ebd096f1dde 100644
--- a/src/test/ui/issues/issue-22872.stderr
+++ b/src/test/ui/issues/issue-22872.stderr
@@ -1,23 +1,14 @@
-error[E0277]: the trait bound `for<'b> P: Process<'b>` is not satisfied
+error[E0277]: `<P as Process<'_>>::Item` is not an iterator
   --> $DIR/issue-22872.rs:20:36
    |
 LL |     let _: Box<for<'b> Wrap<'b>> = Box::new(Wrapper(process));
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'b> Process<'b>` is not implemented for `P`
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^ `<P as Process<'_>>::Item` is not an iterator
    |
-   = help: consider adding a `where for<'b> P: Process<'b>` bound
+   = help: the trait `std::iter::Iterator` is not implemented for `<P as Process<'_>>::Item`
+   = help: consider adding a `where <P as Process<'_>>::Item: std::iter::Iterator` bound
    = note: required because of the requirements on the impl of `for<'b> Wrap<'b>` for `Wrapper<P>`
    = note: required for the cast to the object type `dyn for<'b> Wrap<'b>`
 
-error[E0277]: `<P as Process<'b>>::Item` is not an iterator
-  --> $DIR/issue-22872.rs:20:36
-   |
-LL |     let _: Box<for<'b> Wrap<'b>> = Box::new(Wrapper(process));
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^ `<P as Process<'b>>::Item` is not an iterator
-   |
-   = help: the trait `for<'b> std::iter::Iterator` is not implemented for `<P as Process<'b>>::Item`
-   = note: required because of the requirements on the impl of `for<'b> Wrap<'b>` for `Wrapper<P>`
-   = note: required for the cast to the object type `dyn for<'b> Wrap<'b>`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/issues/issue-40000.rs b/src/test/ui/issues/issue-40000.rs
index 2d1e1d07a26..320992c0764 100644
--- a/src/test/ui/issues/issue-40000.rs
+++ b/src/test/ui/issues/issue-40000.rs
@@ -3,6 +3,5 @@ fn main() {
 
     fn foo(x: Box<Fn(&i32)>) {}
     let bar = Box::new(|x: &i32| {}) as Box<Fn(_)>;
-    foo(bar); //~ ERROR mismatched types
-    //~| expected concrete lifetime, found bound lifetime parameter
+    foo(bar); //~ ERROR E0308
 }
diff --git a/src/test/ui/issues/issue-40000.stderr b/src/test/ui/issues/issue-40000.stderr
index 3ff58703481..d7966cea52b 100644
--- a/src/test/ui/issues/issue-40000.stderr
+++ b/src/test/ui/issues/issue-40000.stderr
@@ -1,11 +1,11 @@
 error[E0308]: mismatched types
   --> $DIR/issue-40000.rs:6:9
    |
-LL |     foo(bar); //~ ERROR mismatched types
-   |         ^^^ expected concrete lifetime, found bound lifetime parameter
+LL |     foo(bar); //~ ERROR E0308
+   |         ^^^ one type is more general than the other
    |
-   = note: expected type `std::boxed::Box<(dyn for<'r> std::ops::Fn(&'r i32) + 'static)>`
-              found type `std::boxed::Box<dyn std::ops::Fn(_)>`
+   = note: expected type `dyn for<'r> std::ops::Fn(&'r i32)`
+              found type `dyn std::ops::Fn(&i32)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-54302-cases.rs b/src/test/ui/issues/issue-54302-cases.rs
index 0c852a2eb63..faa116269ee 100644
--- a/src/test/ui/issues/issue-54302-cases.rs
+++ b/src/test/ui/issues/issue-54302-cases.rs
@@ -61,25 +61,25 @@ impl<T> RefFoo<T> for T where for<'a> &'a T: Foo<'static, T> {
 fn coerce_lifetime1(a: &u32) -> &'static u32
 {
     <u32 as RefFoo<u32>>::ref_foo(a)
-    //~^ ERROR the trait bound `for<'a> &'a u32: Foo2<'_, u32>` is not satisfied
+    //~^ ERROR not general enough
 }
 
 fn coerce_lifetime2(a: &i32) -> &'static i32
 {
     <i32 as RefFoo<i32>>::ref_foo(a)
-    //~^ ERROR the requirement `for<'a> 'a : ` is not satisfied
+    //~^ ERROR not general enough
 }
 
 fn coerce_lifetime3(a: &u64) -> &'static u64
 {
     <u64 as RefFoo<u64>>::ref_foo(a)
-    //~^ ERROR type mismatch resolving `for<'a> <&'a u64 as Mirror>::Image == &u64`
+    //~^ ERROR not general enough
 }
 
 fn coerce_lifetime4(a: &i64) -> &'static i64
 {
     <i64 as RefFoo<i64>>::ref_foo(a)
-    //~^ ERROR type mismatch resolving `for<'a> <&'a i64 as Mirror>::Image == &i64`
+    //~^ ERROR not general enough
 }
 
 fn main() {}
diff --git a/src/test/ui/issues/issue-54302-cases.stderr b/src/test/ui/issues/issue-54302-cases.stderr
index 0ed88463277..c1329d331a1 100644
--- a/src/test/ui/issues/issue-54302-cases.stderr
+++ b/src/test/ui/issues/issue-54302-cases.stderr
@@ -1,65 +1,38 @@
-error[E0277]: the trait bound `for<'a> &'a u32: Foo2<'_, u32>` is not satisfied
+error: implementation of `Foo` is not general enough
   --> $DIR/issue-54302-cases.rs:63:5
    |
 LL |     <u32 as RefFoo<u32>>::ref_foo(a)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Foo2<'_, u32>` is not implemented for `&'a u32`
-   |
-   = help: the following implementations were found:
-             <&'x u32 as Foo2<'x, u32>>
-   = note: required because of the requirements on the impl of `for<'a> Foo<'static, u32>` for `&'a u32`
-   = note: required because of the requirements on the impl of `RefFoo<u32>` for `u32`
-note: required by `RefFoo::ref_foo`
-  --> $DIR/issue-54302-cases.rs:51:5
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL |     fn ref_foo(&self) -> &'static T;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `&'0 u32` must implement `Foo<'static, u32>` for any lifetime `'0`
+   = note: but `&'1 _` only implements `Foo<'_, _>` for the lifetime `'1`
 
-error[E0279]: the requirement `for<'a> 'a : ` is not satisfied (`expected bound lifetime parameter 'a, found concrete lifetime`)
+error: implementation of `Foo` is not general enough
   --> $DIR/issue-54302-cases.rs:69:5
    |
 LL |     <i32 as RefFoo<i32>>::ref_foo(a)
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: required because of the requirements on the impl of `for<'a> Foo2<'_, i32>` for `&'a i32`
-   = note: required because of the requirements on the impl of `for<'a> Foo<'static, i32>` for `&'a i32`
-   = note: required because of the requirements on the impl of `RefFoo<i32>` for `i32`
-note: required by `RefFoo::ref_foo`
-  --> $DIR/issue-54302-cases.rs:51:5
-   |
-LL |     fn ref_foo(&self) -> &'static T;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `&'0 i32` must implement `Foo<'static, i32>` for any lifetime `'0`
+   = note: but `&'1 _` only implements `Foo<'_, _>` for the lifetime `'1`
 
-error[E0271]: type mismatch resolving `for<'a> <&'a u64 as Mirror>::Image == &u64`
+error: implementation of `Foo` is not general enough
   --> $DIR/issue-54302-cases.rs:75:5
    |
 LL |     <u64 as RefFoo<u64>>::ref_foo(a)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'a, found concrete lifetime
-   |
-   = note: required because of the requirements on the impl of `for<'a> Foo2<'_, u64>` for `&'a u64`
-   = note: required because of the requirements on the impl of `for<'a> Foo<'static, u64>` for `&'a u64`
-   = note: required because of the requirements on the impl of `RefFoo<u64>` for `u64`
-note: required by `RefFoo::ref_foo`
-  --> $DIR/issue-54302-cases.rs:51:5
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL |     fn ref_foo(&self) -> &'static T;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `&'0 u64` must implement `Foo<'static, u64>` for any lifetime `'0`
+   = note: but `&'1 _` only implements `Foo<'_, _>` for the lifetime `'1`
 
-error[E0271]: type mismatch resolving `for<'a> <&'a i64 as Mirror>::Image == &i64`
+error: implementation of `Foo` is not general enough
   --> $DIR/issue-54302-cases.rs:81:5
    |
 LL |     <i64 as RefFoo<i64>>::ref_foo(a)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'a, found concrete lifetime
-   |
-   = note: required because of the requirements on the impl of `for<'a> Foo2<'_, i64>` for `&'a i64`
-   = note: required because of the requirements on the impl of `for<'a> Foo<'static, i64>` for `&'a i64`
-   = note: required because of the requirements on the impl of `RefFoo<i64>` for `i64`
-note: required by `RefFoo::ref_foo`
-  --> $DIR/issue-54302-cases.rs:51:5
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL |     fn ref_foo(&self) -> &'static T;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `&'0 i64` must implement `Foo<'static, i64>` for any lifetime `'0`
+   = note: but `&'1 _` only implements `Foo<'_, _>` for the lifetime `'1`
 
 error: aborting due to 4 previous errors
 
-Some errors occurred: E0271, E0277, E0279.
-For more information about an error, try `rustc --explain E0271`.
diff --git a/src/test/ui/issues/issue-54302.rs b/src/test/ui/issues/issue-54302.rs
index c681d37cdae..1bfaebc3895 100644
--- a/src/test/ui/issues/issue-54302.rs
+++ b/src/test/ui/issues/issue-54302.rs
@@ -11,7 +11,7 @@ fn main() {
     // Then why does it implement DeserializeOwned? This compiles.
     fn assert_deserialize_owned<T: DeserializeOwned>() {}
     assert_deserialize_owned::<&'static str>();
-    //~^ ERROR the requirement `for<'de> 'de : ` is not satisfied
+    //~^ ERROR not general enough
 
     // It correctly does not implement for<'de> Deserialize<'de>.
     //fn assert_hrtb<T: for<'de> Deserialize<'de>>() {}
diff --git a/src/test/ui/issues/issue-54302.stderr b/src/test/ui/issues/issue-54302.stderr
index 2d95aa2b156..1b255204b6e 100644
--- a/src/test/ui/issues/issue-54302.stderr
+++ b/src/test/ui/issues/issue-54302.stderr
@@ -1,17 +1,11 @@
-error[E0279]: the requirement `for<'de> 'de : ` is not satisfied (`expected bound lifetime parameter 'de, found concrete lifetime`)
+error: implementation of `Deserialize` is not general enough
   --> $DIR/issue-54302.rs:13:5
    |
 LL |     assert_deserialize_owned::<&'static str>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: required because of the requirements on the impl of `for<'de> Deserialize<'de>` for `&'static str`
-   = note: required because of the requirements on the impl of `DeserializeOwned` for `&'static str`
-note: required by `main::assert_deserialize_owned`
-  --> $DIR/issue-54302.rs:12:5
-   |
-LL |     fn assert_deserialize_owned<T: DeserializeOwned>() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `&'static str` must implement `Deserialize<'0>` for any lifetime `'0`
+   = note: but `&str` only implements `Deserialize<'1>` for some lifetime `'1`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0279`.
diff --git a/src/test/ui/lub-glb/old-lub-glb-hr.rs b/src/test/ui/lub-glb/old-lub-glb-hr.rs
index 04990546776..324dc86bd92 100644
--- a/src/test/ui/lub-glb/old-lub-glb-hr.rs
+++ b/src/test/ui/lub-glb/old-lub-glb-hr.rs
@@ -1,11 +1,16 @@
 // Test that we give a note when the old LUB/GLB algorithm would have
-// succeeded but the new code (which is stricter) gives an error.
+// succeeded but the new code (which requires equality) gives an
+// error. However, now that we handle subtyping correctly, we no
+// longer get an error, because we recognize these two types as
+// equivalent!
+//
+// compile-pass
 
 fn foo(
     x: fn(&u8, &u8),
     y: for<'a> fn(&'a u8, &'a u8),
 ) {
-    let z = match 22 { //~ ERROR incompatible types
+    let z = match 22 {
         0 => x,
         _ => y,
     };
diff --git a/src/test/ui/lub-glb/old-lub-glb-hr.stderr b/src/test/ui/lub-glb/old-lub-glb-hr.stderr
deleted file mode 100644
index 9c397a8fd2c..00000000000
--- a/src/test/ui/lub-glb/old-lub-glb-hr.stderr
+++ /dev/null
@@ -1,17 +0,0 @@
-error[E0308]: match arms have incompatible types
-  --> $DIR/old-lub-glb-hr.rs:8:13
-   |
-LL |       let z = match 22 { //~ ERROR incompatible types
-   |  _____________^
-LL | |         0 => x,
-LL | |         _ => y,
-   | |              - match arm with an incompatible type
-LL | |     };
-   | |_____^ expected bound lifetime parameter, found concrete lifetime
-   |
-   = note: expected type `for<'r, 's> fn(&'r u8, &'s u8)`
-              found type `for<'a> fn(&'a u8, &'a u8)`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/lub-glb/old-lub-glb-object.rs b/src/test/ui/lub-glb/old-lub-glb-object.rs
index 309f2d0ea76..9be7a813603 100644
--- a/src/test/ui/lub-glb/old-lub-glb-object.rs
+++ b/src/test/ui/lub-glb/old-lub-glb-object.rs
@@ -7,7 +7,7 @@ fn foo(
     x: &for<'a, 'b> Foo<&'a u8, &'b u8>,
     y: &for<'a> Foo<&'a u8, &'a u8>,
 ) {
-    let z = match 22 { //~ ERROR incompatible types
+    let z = match 22 { //~ ERROR E0308
         0 => x,
         _ => y,
     };
diff --git a/src/test/ui/lub-glb/old-lub-glb-object.stderr b/src/test/ui/lub-glb/old-lub-glb-object.stderr
index 243414c19c0..17d3648156b 100644
--- a/src/test/ui/lub-glb/old-lub-glb-object.stderr
+++ b/src/test/ui/lub-glb/old-lub-glb-object.stderr
@@ -1,16 +1,15 @@
-error[E0308]: match arms have incompatible types
+error[E0308]: mismatched types
   --> $DIR/old-lub-glb-object.rs:10:13
    |
-LL |       let z = match 22 { //~ ERROR incompatible types
+LL |       let z = match 22 { //~ ERROR E0308
    |  _____________^
 LL | |         0 => x,
 LL | |         _ => y,
-   | |              - match arm with an incompatible type
 LL | |     };
-   | |_____^ expected bound lifetime parameter 'a, found concrete lifetime
+   | |_____^ one type is more general than the other
    |
-   = note: expected type `&dyn for<'a, 'b> Foo<&'a u8, &'b u8>`
-              found type `&dyn for<'a> Foo<&'a u8, &'a u8>`
+   = note: expected type `dyn for<'a, 'b> Foo<&'a u8, &'b u8>`
+              found type `dyn for<'a> Foo<&'a u8, &'a u8>`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs b/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs
index c5d9e4b9b76..437150666be 100644
--- a/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs
+++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs
@@ -7,6 +7,6 @@ fn main() {
 
 fn baz<F: Fn(*mut &u32)>(_: F) {}
 fn _test<'a>(f: fn(*mut &'a u32)) {
-    baz(f); //~ ERROR type mismatch
-     //~^ ERROR type mismatch
+    baz(f); //~ ERROR mismatched types
+     //~| ERROR mismatched types
 }
diff --git a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr
index 5dd6887005e..a6628006587 100644
--- a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr
+++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr
@@ -22,34 +22,25 @@ LL |     a.iter().map(|_: (u16, u16)| 45); //~ ERROR type mismatch
    |              |
    |              expected signature of `fn(&(u32, u32)) -> _`
 
-error[E0631]: type mismatch in function arguments
+error[E0308]: mismatched types
   --> $DIR/closure-arg-type-mismatch.rs:10:5
    |
-LL |     baz(f); //~ ERROR type mismatch
-   |     ^^^
-   |     |
-   |     expected signature of `for<'r> fn(*mut &'r u32) -> _`
-   |     found signature of `fn(*mut &'a u32) -> _`
+LL |     baz(f); //~ ERROR mismatched types
+   |     ^^^ one type is more general than the other
    |
-note: required by `baz`
-  --> $DIR/closure-arg-type-mismatch.rs:8:1
-   |
-LL | fn baz<F: Fn(*mut &u32)>(_: F) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: expected type `for<'r> std::ops::Fn<(*mut &'r u32,)>`
+              found type `std::ops::Fn<(*mut &'a u32,)>`
 
-error[E0271]: type mismatch resolving `for<'r> <fn(*mut &'a u32) as std::ops::FnOnce<(*mut &'r u32,)>>::Output == ()`
+error[E0308]: mismatched types
   --> $DIR/closure-arg-type-mismatch.rs:10:5
    |
-LL |     baz(f); //~ ERROR type mismatch
-   |     ^^^ expected bound lifetime parameter, found concrete lifetime
-   |
-note: required by `baz`
-  --> $DIR/closure-arg-type-mismatch.rs:8:1
+LL |     baz(f); //~ ERROR mismatched types
+   |     ^^^ one type is more general than the other
    |
-LL | fn baz<F: Fn(*mut &u32)>(_: F) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: expected type `std::ops::FnOnce<(*mut &u32,)>`
+              found type `std::ops::FnOnce<(*mut &'a u32,)>`
 
 error: aborting due to 5 previous errors
 
-Some errors occurred: E0271, E0631.
-For more information about an error, try `rustc --explain E0271`.
+Some errors occurred: E0308, E0631.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/mismatched_types/closure-mismatch.rs b/src/test/ui/mismatched_types/closure-mismatch.rs
index 40a4641fe71..152a5254937 100644
--- a/src/test/ui/mismatched_types/closure-mismatch.rs
+++ b/src/test/ui/mismatched_types/closure-mismatch.rs
@@ -5,6 +5,5 @@ impl<T: Fn(&())> Foo for T {}
 fn baz<T: Foo>(_: T) {}
 
 fn main() {
-    baz(|_| ()); //~ ERROR type mismatch
-    //~^ ERROR type mismatch
+    baz(|_| ()); //~ ERROR E0308
 }
diff --git a/src/test/ui/mismatched_types/closure-mismatch.stderr b/src/test/ui/mismatched_types/closure-mismatch.stderr
index e55047e96c2..0d87bc22875 100644
--- a/src/test/ui/mismatched_types/closure-mismatch.stderr
+++ b/src/test/ui/mismatched_types/closure-mismatch.stderr
@@ -1,32 +1,12 @@
-error[E0271]: type mismatch resolving `for<'r> <[closure@$DIR/closure-mismatch.rs:8:9: 8:15] as std::ops::FnOnce<(&'r (),)>>::Output == ()`
+error[E0308]: mismatched types
   --> $DIR/closure-mismatch.rs:8:5
    |
-LL |     baz(|_| ()); //~ ERROR type mismatch
-   |     ^^^ expected bound lifetime parameter, found concrete lifetime
+LL |     baz(|_| ()); //~ ERROR E0308
+   |     ^^^ one type is more general than the other
    |
-   = note: required because of the requirements on the impl of `Foo` for `[closure@$DIR/closure-mismatch.rs:8:9: 8:15]`
-note: required by `baz`
-  --> $DIR/closure-mismatch.rs:5:1
-   |
-LL | fn baz<T: Foo>(_: T) {}
-   | ^^^^^^^^^^^^^^^^^^^^
-
-error[E0631]: type mismatch in closure arguments
-  --> $DIR/closure-mismatch.rs:8:5
-   |
-LL |     baz(|_| ()); //~ ERROR type mismatch
-   |     ^^^ ------ found signature of `fn(_) -> _`
-   |     |
-   |     expected signature of `for<'r> fn(&'r ()) -> _`
-   |
-   = note: required because of the requirements on the impl of `Foo` for `[closure@$DIR/closure-mismatch.rs:8:9: 8:15]`
-note: required by `baz`
-  --> $DIR/closure-mismatch.rs:5:1
-   |
-LL | fn baz<T: Foo>(_: T) {}
-   | ^^^^^^^^^^^^^^^^^^^^
+   = note: expected type `for<'r> std::ops::Fn<(&'r (),)>`
+              found type `std::ops::Fn<(&(),)>`
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0271, E0631.
-For more information about an error, try `rustc --explain E0271`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/regions-fn-subtyping-return-static-fail.rs b/src/test/ui/regions-fn-subtyping-return-static-fail.rs
index a8fe562656e..242119cc201 100644
--- a/src/test/ui/regions-fn-subtyping-return-static-fail.rs
+++ b/src/test/ui/regions-fn-subtyping-return-static-fail.rs
@@ -37,8 +37,7 @@ fn baz(x: &S) -> &S {
 fn supply_F() {
     want_F(foo);
 
-    // FIXME(#33684) -- this should be a subtype, but current alg. rejects it incorrectly
-    want_F(bar); //~ ERROR E0308
+    want_F(bar);
 
     want_F(baz);
 }
@@ -46,11 +45,7 @@ fn supply_F() {
 fn supply_G() {
     want_G(foo);
     want_G(bar);
-    want_G(baz);
-    //~^ ERROR mismatched types
-    //~| expected type `for<'cx> fn(&'cx S) -> &'static S`
-    //~| found type `for<'r> fn(&'r S) -> &'r S {baz}`
-    //~| expected concrete lifetime, found bound lifetime parameter 'cx
+    want_G(baz); //~ ERROR
 }
 
 pub fn main() {
diff --git a/src/test/ui/regions-fn-subtyping-return-static-fail.stderr b/src/test/ui/regions-fn-subtyping-return-static-fail.stderr
index 0bf6e0514d2..a9234e43191 100644
--- a/src/test/ui/regions-fn-subtyping-return-static-fail.stderr
+++ b/src/test/ui/regions-fn-subtyping-return-static-fail.stderr
@@ -1,21 +1,12 @@
 error[E0308]: mismatched types
-  --> $DIR/regions-fn-subtyping-return-static-fail.rs:41:12
+  --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:12
    |
-LL |     want_F(bar); //~ ERROR E0308
-   |            ^^^ expected concrete lifetime, found bound lifetime parameter 'cx
-   |
-   = note: expected type `for<'cx> fn(&'cx S) -> &'cx S`
-              found type `for<'a> fn(&'a S) -> &S {bar::<'_>}`
-
-error[E0308]: mismatched types
-  --> $DIR/regions-fn-subtyping-return-static-fail.rs:49:12
-   |
-LL |     want_G(baz);
-   |            ^^^ expected concrete lifetime, found bound lifetime parameter 'cx
+LL |     want_G(baz); //~ ERROR
+   |            ^^^ one type is more general than the other
    |
    = note: expected type `for<'cx> fn(&'cx S) -> &'static S`
-              found type `for<'r> fn(&'r S) -> &'r S {baz}`
+              found type `for<'r> fn(&'r S) -> &'r S`
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr
index 5c8b3d3ba69..47e1d0efdc7 100644
--- a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr
+++ b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr
@@ -20,10 +20,10 @@ error[E0308]: mismatched types
   --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:43
    |
 LL |     let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR mismatched types
-   |                                           ^ expected concrete lifetime, found bound lifetime parameter
+   |                                           ^ one type is more general than the other
    |
    = note: expected type `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)`
-              found type `for<'r, 's> fn(&'r mut &isize, &'s mut &isize) {a::<'_, '_>}`
+              found type `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs
index 8e7f4ccad9c..066522548ad 100644
--- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs
+++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.rs
@@ -19,7 +19,7 @@ fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
 fn d() {
     // 'a and 'b are early bound in the function `a` because they appear
     // inconstraints:
-    let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; //~ ERROR mismatched types
+    let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; //~ ERROR E0308
 }
 
 fn e() {
diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr
index 01899349bf7..1e7b99053f7 100644
--- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr
+++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr
@@ -30,11 +30,11 @@ LL |     a(x, y, z); //~ ERROR lifetime mismatch [E0623]
 error[E0308]: mismatched types
   --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:56
    |
-LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; //~ ERROR mismatched types
-   |                                                        ^ expected concrete lifetime, found bound lifetime parameter
+LL |     let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; //~ ERROR E0308
+   |                                                        ^ one type is more general than the other
    |
    = note: expected type `for<'r, 's, 't0, 't1, 't2, 't3> fn(&'r mut &'s isize, &'t0 mut &'t1 isize, &'t2 mut &'t3 isize)`
-              found type `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize) {a::<'_, '_, '_>}`
+              found type `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize)`
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static.rs b/src/test/ui/regions/regions-fn-subtyping-return-static.rs
index fa5f6a334b0..4d6d342f571 100644
--- a/src/test/ui/regions/regions-fn-subtyping-return-static.rs
+++ b/src/test/ui/regions/regions-fn-subtyping-return-static.rs
@@ -5,6 +5,8 @@
 // *ANY* lifetime and returns a reference with the 'static lifetime.
 // This can safely be considered to be an instance of `F` because all
 // lifetimes are sublifetimes of 'static.
+//
+// compile-pass
 
 #![allow(dead_code)]
 #![allow(unused_variables)]
@@ -37,8 +39,7 @@ fn baz(x: &S) -> &S {
 fn supply_F() {
     want_F(foo);
 
-    // FIXME(#33684) -- this should be a subtype, but current alg. rejects it incorrectly
-    want_F(bar); //~ ERROR E0308
+    want_F(bar);
 
     want_F(baz);
 }
diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static.stderr b/src/test/ui/regions/regions-fn-subtyping-return-static.stderr
index 30646fe7965..61eaf9fcf10 100644
--- a/src/test/ui/regions/regions-fn-subtyping-return-static.stderr
+++ b/src/test/ui/regions/regions-fn-subtyping-return-static.stderr
@@ -1,12 +1,26 @@
-error[E0308]: mismatched types
-  --> $DIR/regions-fn-subtyping-return-static.rs:41:12
+warning: function `want_F` should have a snake case name such as `want_f`
+  --> $DIR/regions-fn-subtyping-return-static.rs:18:1
    |
-LL |     want_F(bar); //~ ERROR E0308
-   |            ^^^ expected concrete lifetime, found bound lifetime parameter 'cx
+LL | fn want_F(f: F) { }
+   | ^^^^^^^^^^^^^^^^^^^
    |
-   = note: expected type `for<'cx> fn(&'cx S) -> &'cx S`
-              found type `for<'a> fn(&'a S) -> &S {bar::<'_>}`
+   = note: #[warn(non_snake_case)] on by default
 
-error: aborting due to previous error
+warning: function `want_G` should have a snake case name such as `want_g`
+  --> $DIR/regions-fn-subtyping-return-static.rs:22:1
+   |
+LL | fn want_G(f: G) { }
+   | ^^^^^^^^^^^^^^^^^^^
+
+warning: function `supply_F` should have a snake case name such as `supply_f`
+  --> $DIR/regions-fn-subtyping-return-static.rs:39:1
+   |
+LL | / fn supply_F() {
+LL | |     want_F(foo);
+LL | |
+LL | |     want_F(bar);
+LL | |
+LL | |     want_F(baz);
+LL | | }
+   | |_^
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.rs b/src/test/ui/regions/regions-lifetime-bounds-on-fns.rs
index 2f35883cc99..7d7f62e1979 100644
--- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.rs
+++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.rs
@@ -17,7 +17,7 @@ fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
 fn d() {
     // 'a and 'b are early bound in the function `a` because they appear
     // inconstraints:
-    let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR mismatched types
+    let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR E0308
 }
 
 fn e() {
diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr b/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr
index c4d4a159a9c..a43ee7ec3ac 100644
--- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr
+++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr
@@ -19,11 +19,11 @@ LL |     a(x, y); //~ ERROR lifetime mismatch [E0623]
 error[E0308]: mismatched types
   --> $DIR/regions-lifetime-bounds-on-fns.rs:20:43
    |
-LL |     let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR mismatched types
-   |                                           ^ expected concrete lifetime, found bound lifetime parameter
+LL |     let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR E0308
+   |                                           ^ one type is more general than the other
    |
    = note: expected type `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)`
-              found type `for<'r, 's> fn(&'r mut &isize, &'s mut &isize) {a::<'_, '_>}`
+              found type `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/where-clauses/where-for-self-2.rs b/src/test/ui/where-clauses/where-for-self-2.rs
index 10b25c73403..0ce38e69f6b 100644
--- a/src/test/ui/where-clauses/where-for-self-2.rs
+++ b/src/test/ui/where-clauses/where-for-self-2.rs
@@ -18,6 +18,5 @@ fn foo<T>(x: &T)
 {}
 
 fn main() {
-    foo(&X);
-    //~^ error: `for<'a> &'a _: Bar` is not satisfied
+    foo(&X); //~ ERROR implementation of `Bar` is not general enough
 }
diff --git a/src/test/ui/where-clauses/where-for-self-2.stderr b/src/test/ui/where-clauses/where-for-self-2.stderr
index dbe68b82c24..afc80bf4d8e 100644
--- a/src/test/ui/where-clauses/where-for-self-2.stderr
+++ b/src/test/ui/where-clauses/where-for-self-2.stderr
@@ -1,19 +1,12 @@
-error[E0277]: the trait bound `for<'a> &'a _: Bar` is not satisfied
+error: implementation of `Bar` is not general enough
   --> $DIR/where-for-self-2.rs:21:5
    |
-LL |     foo(&X);
-   |     ^^^ the trait `for<'a> Bar` is not implemented for `&'a _`
+LL |     foo(&X); //~ ERROR implementation of `Bar` is not general enough
+   |     ^^^
    |
-   = help: the following implementations were found:
-             <&'static u32 as Bar>
-note: required by `foo`
-  --> $DIR/where-for-self-2.rs:16:1
-   |
-LL | / fn foo<T>(x: &T)
-LL | |     where for<'a> &'a T: Bar
-LL | | {}
-   | |__^
+   = note: Due to a where-clause on `foo`,
+   = note: `&'0 _` must implement `Bar` for any lifetime `'0`
+   = note: but `&'1 u32` only implements `Bar` for the lifetime `'1`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0277`.