about summary refs log tree commit diff
diff options
context:
space:
mode:
-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`.