about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-09-14 19:26:18 +0530
committerGitHub <noreply@github.com>2022-09-14 19:26:18 +0530
commit430123164ffefd3fef8cb8f45bf9aca931d9ba6c (patch)
treec64bdc20db467e9f10e4b36818022e911d83340b
parentc97922dca563cb7f9385b18dbf7ca8c97f8e1597 (diff)
parentaae37f87632dd74856d55c0cd45d2c192379c990 (diff)
downloadrust-430123164ffefd3fef8cb8f45bf9aca931d9ba6c.tar.gz
rust-430123164ffefd3fef8cb8f45bf9aca931d9ba6c.zip
Rollup merge of #101433 - jackh726:better-static-placeholder-error, r=compiler-errors
Emit a note that static bounds from HRTBs are a bug

This note isn't perfect, but opening this to either 1) land as is or 2) get some feedback on how to improve it

Let r? `@compiler-errors` and cc. `@nikomatsakis`
-rw-r--r--compiler/rustc_borrowck/src/constraints/mod.rs7
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs32
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs17
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs141
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs26
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs31
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs6
-rw-r--r--compiler/rustc_error_messages/locales/en-US/infer.ftl1
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs68
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs28
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs24
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs31
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs7
-rw-r--r--compiler/rustc_middle/src/mir/query.rs4
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs13
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs16
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs2
-rw-r--r--compiler/rustc_traits/src/type_op.rs40
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs10
-rw-r--r--src/test/ui/generic-associated-types/bugs/hrtb-implied-1.rs35
-rw-r--r--src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr20
-rw-r--r--src/test/ui/generic-associated-types/bugs/hrtb-implied-2.rs40
-rw-r--r--src/test/ui/generic-associated-types/bugs/hrtb-implied-2.stderr22
-rw-r--r--src/test/ui/generic-associated-types/bugs/hrtb-implied-3.rs23
-rw-r--r--src/test/ui/generic-associated-types/bugs/hrtb-implied-3.stderr22
-rw-r--r--src/test/ui/generic-associated-types/trait-objects.extended.stderr2
-rw-r--r--src/test/ui/higher-rank-trait-bounds/hrtb-just-for-static.stderr6
-rw-r--r--src/test/ui/higher-rank-trait-bounds/hrtb-perfect-forwarding.stderr6
-rw-r--r--src/test/ui/issues/issue-26217.stderr6
-rw-r--r--src/test/ui/nll/local-outlives-static-via-hrtb.stderr12
-rw-r--r--src/test/ui/nll/type-test-universe.stderr6
34 files changed, 543 insertions, 173 deletions
diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs
index c94dfe39b69..df04128135b 100644
--- a/compiler/rustc_borrowck/src/constraints/mod.rs
+++ b/compiler/rustc_borrowck/src/constraints/mod.rs
@@ -21,10 +21,7 @@ pub(crate) struct OutlivesConstraintSet<'tcx> {
 
 impl<'tcx> OutlivesConstraintSet<'tcx> {
     pub(crate) fn push(&mut self, constraint: OutlivesConstraint<'tcx>) {
-        debug!(
-            "OutlivesConstraintSet::push({:?}: {:?} @ {:?}",
-            constraint.sup, constraint.sub, constraint.locations
-        );
+        debug!("OutlivesConstraintSet::push({:?})", constraint);
         if constraint.sup == constraint.sub {
             // 'a: 'a is pretty uninteresting
             return;
@@ -73,7 +70,7 @@ impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {
     }
 }
 
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq)]
 pub struct OutlivesConstraint<'tcx> {
     // NB. The ordering here is not significant for correctness, but
     // it is for convenience. Before we dump the constraints in the
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 2f61849c383..1c01e78abd4 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -15,7 +15,7 @@ use rustc_middle::ty::{self, RegionVid, TyCtxt};
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{sym, DesugaringKind, Span};
 
-use crate::region_infer::BlameConstraint;
+use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
 use crate::{
     borrow_set::BorrowData, nll::ConstraintDescription, region_infer::Cause, MirBorrowckCtxt,
     WriteKind,
@@ -38,6 +38,7 @@ pub(crate) enum BorrowExplanation<'tcx> {
         span: Span,
         region_name: RegionName,
         opt_place_desc: Option<String>,
+        extra_info: Vec<ExtraConstraintInfo>,
     },
     Unexplained,
 }
@@ -243,6 +244,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
                 ref region_name,
                 ref opt_place_desc,
                 from_closure: _,
+                ref extra_info,
             } => {
                 region_name.highlight_region_name(err);
 
@@ -268,6 +270,14 @@ impl<'tcx> BorrowExplanation<'tcx> {
                     );
                 };
 
+                for extra in extra_info {
+                    match extra {
+                        ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
+                            err.span_note(*span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime"));
+                        }
+                    }
+                }
+
                 self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
             }
             _ => {}
@@ -309,18 +319,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         borrow_region: RegionVid,
         outlived_region: RegionVid,
-    ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>) {
-        let BlameConstraint { category, from_closure, cause, variance_info: _ } =
-            self.regioncx.best_blame_constraint(
-                &self.body,
-                borrow_region,
-                NllRegionVariableOrigin::FreeRegion,
-                |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
-            );
+    ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>) {
+        let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint(
+            borrow_region,
+            NllRegionVariableOrigin::FreeRegion,
+            |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
+        );
+        let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
 
         let outlived_fr_name = self.give_region_a_name(outlived_region);
 
-        (category, from_closure, cause.span, outlived_fr_name)
+        (category, from_closure, cause.span, outlived_fr_name, extra_info)
     }
 
     /// Returns structured explanation for *why* the borrow contains the
@@ -392,7 +401,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
             None => {
                 if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
-                    let (category, from_closure, span, region_name) =
+                    let (category, from_closure, span, region_name, extra_info) =
                         self.free_region_constraint_info(borrow_region_vid, region);
                     if let Some(region_name) = region_name {
                         let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref());
@@ -402,6 +411,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             span,
                             region_name,
                             opt_place_desc,
+                            extra_info,
                         }
                     } else {
                         debug!("Could not generate a region name");
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 9615025fa57..34be2874fcb 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -31,7 +31,7 @@ use crate::session_diagnostics::{
 };
 
 use super::{OutlivesSuggestionBuilder, RegionName};
-use crate::region_infer::BlameConstraint;
+use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
 use crate::{
     nll::ConstraintDescription,
     region_infer::{values::RegionElement, TypeTest},
@@ -234,7 +234,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
                     // Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
                     let (_, cause) = self.regioncx.find_outlives_blame_span(
-                        &self.body,
                         longer_fr,
                         NllRegionVariableOrigin::Placeholder(placeholder),
                         error_vid,
@@ -355,10 +354,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     ) {
         debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
 
-        let BlameConstraint { category, cause, variance_info, from_closure: _ } =
-            self.regioncx.best_blame_constraint(&self.body, fr, fr_origin, |r| {
+        let (blame_constraint, extra_info) =
+            self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
                 self.regioncx.provides_universal_region(r, fr, outlived_fr)
             });
+        let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
 
         debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
 
@@ -467,6 +467,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
         }
 
+        for extra in extra_info {
+            match extra {
+                ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
+                    diag.span_note(span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime"));
+                }
+            }
+        }
+
         self.buffer_error(diag);
     }
 
@@ -558,6 +566,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     /// LL |     ref_obj(x)
     ///    |     ^^^^^^^^^^ `x` escapes the function body here
     /// ```
+    #[instrument(level = "debug", skip(self))]
     fn report_escaping_data_error(
         &self,
         errci: &ErrorConstraintInfo<'tcx>,
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index de70b17e44c..244e6e3422d 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -245,6 +245,11 @@ enum Trace<'tcx> {
     NotVisited,
 }
 
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum ExtraConstraintInfo {
+    PlaceholderFromPredicate(Span),
+}
+
 impl<'tcx> RegionInferenceContext<'tcx> {
     /// Creates a new region inference context with a total of
     /// `num_region_variables` valid inference variables; the first N
@@ -590,13 +595,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // constraints were too strong, and if so, emit or propagate those errors.
         if infcx.tcx.sess.opts.unstable_opts.polonius {
             self.check_polonius_subset_errors(
-                body,
                 outlives_requirements.as_mut(),
                 &mut errors_buffer,
                 polonius_output.expect("Polonius output is unavailable despite `-Z polonius`"),
             );
         } else {
-            self.check_universal_regions(body, outlives_requirements.as_mut(), &mut errors_buffer);
+            self.check_universal_regions(outlives_requirements.as_mut(), &mut errors_buffer);
         }
 
         if errors_buffer.is_empty() {
@@ -1409,7 +1413,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// report them as errors.
     fn check_universal_regions(
         &self,
-        body: &Body<'tcx>,
         mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
         errors_buffer: &mut RegionErrors<'tcx>,
     ) {
@@ -1420,7 +1423,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     // they did not grow too large, accumulating any requirements
                     // for our caller into the `outlives_requirements` vector.
                     self.check_universal_region(
-                        body,
                         fr,
                         &mut propagated_outlives_requirements,
                         errors_buffer,
@@ -1461,7 +1463,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// report them as errors.
     fn check_polonius_subset_errors(
         &self,
-        body: &Body<'tcx>,
         mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
         errors_buffer: &mut RegionErrors<'tcx>,
         polonius_output: Rc<PoloniusOutput>,
@@ -1508,7 +1509,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             let propagated = self.try_propagate_universal_region_error(
                 *longer_fr,
                 *shorter_fr,
-                body,
                 &mut propagated_outlives_requirements,
             );
             if propagated == RegionRelationCheckResult::Error {
@@ -1548,13 +1548,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ///
     /// Things that are to be propagated are accumulated into the
     /// `outlives_requirements` vector.
-    #[instrument(
-        skip(self, body, propagated_outlives_requirements, errors_buffer),
-        level = "debug"
-    )]
+    #[instrument(skip(self, propagated_outlives_requirements, errors_buffer), level = "debug")]
     fn check_universal_region(
         &self,
-        body: &Body<'tcx>,
         longer_fr: RegionVid,
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
         errors_buffer: &mut RegionErrors<'tcx>,
@@ -1577,7 +1573,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             if let RegionRelationCheckResult::Error = self.check_universal_region_relation(
                 longer_fr,
                 representative,
-                body,
                 propagated_outlives_requirements,
             ) {
                 errors_buffer.push(RegionErrorKind::RegionError {
@@ -1597,7 +1592,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             if let RegionRelationCheckResult::Error = self.check_universal_region_relation(
                 longer_fr,
                 shorter_fr,
-                body,
                 propagated_outlives_requirements,
             ) {
                 // We only report the first region error. Subsequent errors are hidden so as
@@ -1622,7 +1616,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         longer_fr: RegionVid,
         shorter_fr: RegionVid,
-        body: &Body<'tcx>,
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
     ) -> RegionRelationCheckResult {
         // If it is known that `fr: o`, carry on.
@@ -1638,7 +1631,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             self.try_propagate_universal_region_error(
                 longer_fr,
                 shorter_fr,
-                body,
                 propagated_outlives_requirements,
             )
         }
@@ -1650,7 +1642,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         longer_fr: RegionVid,
         shorter_fr: RegionVid,
-        body: &Body<'tcx>,
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
     ) -> RegionRelationCheckResult {
         if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
@@ -1662,7 +1653,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus);
 
                 let blame_span_category = self.find_outlives_blame_span(
-                    body,
                     longer_fr,
                     NllRegionVariableOrigin::FreeRegion,
                     shorter_fr,
@@ -1816,50 +1806,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
     pub(crate) fn retrieve_closure_constraint_info(
         &self,
-        _body: &Body<'tcx>,
-        constraint: &OutlivesConstraint<'tcx>,
-    ) -> BlameConstraint<'tcx> {
-        let loc = match constraint.locations {
-            Locations::All(span) => {
-                return BlameConstraint {
-                    category: constraint.category,
-                    from_closure: false,
-                    cause: ObligationCause::dummy_with_span(span),
-                    variance_info: constraint.variance_info,
-                };
+        constraint: OutlivesConstraint<'tcx>,
+    ) -> Option<(ConstraintCategory<'tcx>, Span)> {
+        match constraint.locations {
+            Locations::All(_) => None,
+            Locations::Single(loc) => {
+                self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub)).copied()
             }
-            Locations::Single(loc) => loc,
-        };
-
-        let opt_span_category =
-            self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub));
-        opt_span_category
-            .map(|&(category, span)| BlameConstraint {
-                category,
-                from_closure: true,
-                cause: ObligationCause::dummy_with_span(span),
-                variance_info: constraint.variance_info,
-            })
-            .unwrap_or(BlameConstraint {
-                category: constraint.category,
-                from_closure: false,
-                cause: ObligationCause::dummy_with_span(constraint.span),
-                variance_info: constraint.variance_info,
-            })
+        }
     }
 
     /// Finds a good `ObligationCause` to blame for the fact that `fr1` outlives `fr2`.
     pub(crate) fn find_outlives_blame_span(
         &self,
-        body: &Body<'tcx>,
         fr1: RegionVid,
         fr1_origin: NllRegionVariableOrigin,
         fr2: RegionVid,
     ) -> (ConstraintCategory<'tcx>, ObligationCause<'tcx>) {
-        let BlameConstraint { category, cause, .. } =
-            self.best_blame_constraint(body, fr1, fr1_origin, |r| {
-                self.provides_universal_region(r, fr1, fr2)
-            });
+        let BlameConstraint { category, cause, .. } = self
+            .best_blame_constraint(fr1, fr1_origin, |r| self.provides_universal_region(r, fr1, fr2))
+            .0;
         (category, cause)
     }
 
@@ -2045,11 +2011,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     #[instrument(level = "debug", skip(self, target_test))]
     pub(crate) fn best_blame_constraint(
         &self,
-        body: &Body<'tcx>,
         from_region: RegionVid,
         from_region_origin: NllRegionVariableOrigin,
         target_test: impl Fn(RegionVid) -> bool,
-    ) -> BlameConstraint<'tcx> {
+    ) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
         // Find all paths
         let (path, target_region) =
             self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
@@ -2065,6 +2030,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 .collect::<Vec<_>>()
         );
 
+        let mut extra_info = vec![];
+        for constraint in path.iter() {
+            let outlived = constraint.sub;
+            let Some(origin) = self.var_infos.get(outlived) else { continue; };
+            let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(p)) = origin.origin else { continue; };
+            debug!(?constraint, ?p);
+            let ConstraintCategory::Predicate(span) = constraint.category else { continue; };
+            extra_info.push(ExtraConstraintInfo::PlaceholderFromPredicate(span));
+            // We only want to point to one
+            break;
+        }
+
         // We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint.
         // Instead, we use it to produce an improved `ObligationCauseCode`.
         // FIXME - determine what we should do if we encounter multiple `ConstraintCategory::Predicate`
@@ -2090,19 +2067,29 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let mut categorized_path: Vec<BlameConstraint<'tcx>> = path
             .iter()
             .map(|constraint| {
-                if constraint.category == ConstraintCategory::ClosureBounds {
-                    self.retrieve_closure_constraint_info(body, &constraint)
-                } else {
-                    BlameConstraint {
-                        category: constraint.category,
-                        from_closure: false,
-                        cause: ObligationCause::new(
-                            constraint.span,
-                            CRATE_HIR_ID,
-                            cause_code.clone(),
-                        ),
-                        variance_info: constraint.variance_info,
-                    }
+                let (category, span, from_closure, cause_code) =
+                    if constraint.category == ConstraintCategory::ClosureBounds {
+                        if let Some((category, span)) =
+                            self.retrieve_closure_constraint_info(*constraint)
+                        {
+                            (category, span, true, ObligationCauseCode::MiscObligation)
+                        } else {
+                            (
+                                constraint.category,
+                                constraint.span,
+                                false,
+                                ObligationCauseCode::MiscObligation,
+                            )
+                        }
+                    } else {
+                        (constraint.category, constraint.span, false, cause_code.clone())
+                    };
+                BlameConstraint {
+                    category,
+                    from_closure,
+                    cause: ObligationCause::new(span, CRATE_HIR_ID, cause_code),
+                    variance_info: constraint.variance_info,
+                    outlives_constraint: *constraint,
                 }
             })
             .collect();
@@ -2204,7 +2191,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let best_choice =
             if blame_source { range.rev().find(find_region) } else { range.find(find_region) };
 
-        debug!(?best_choice, ?blame_source);
+        debug!(?best_choice, ?blame_source, ?extra_info);
 
         if let Some(i) = best_choice {
             if let Some(next) = categorized_path.get(i + 1) {
@@ -2213,7 +2200,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 {
                     // The return expression is being influenced by the return type being
                     // impl Trait, point at the return type and not the return expr.
-                    return next.clone();
+                    return (next.clone(), extra_info);
                 }
             }
 
@@ -2233,7 +2220,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 }
             }
 
-            return categorized_path[i].clone();
+            return (categorized_path[i].clone(), extra_info);
         }
 
         // If that search fails, that is.. unusual. Maybe everything
@@ -2243,7 +2230,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         categorized_path.sort_by(|p0, p1| p0.category.cmp(&p1.category));
         debug!("sorted_path={:#?}", categorized_path);
 
-        categorized_path.remove(0)
+        (categorized_path.remove(0), extra_info)
     }
 
     pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
@@ -2325,7 +2312,13 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
                              outlives_requirement={:?}",
                             region, outlived_region, outlives_requirement,
                         );
-                        ty::Binder::dummy(ty::OutlivesPredicate(region.into(), outlived_region))
+                        (
+                            ty::Binder::dummy(ty::OutlivesPredicate(
+                                region.into(),
+                                outlived_region,
+                            )),
+                            ConstraintCategory::BoringNoLocation,
+                        )
                     }
 
                     ClosureOutlivesSubject::Ty(ty) => {
@@ -2335,7 +2328,10 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
                              outlives_requirement={:?}",
                             ty, outlived_region, outlives_requirement,
                         );
-                        ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), outlived_region))
+                        (
+                            ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), outlived_region)),
+                            ConstraintCategory::BoringNoLocation,
+                        )
                     }
                 }
             })
@@ -2349,4 +2345,5 @@ pub struct BlameConstraint<'tcx> {
     pub from_closure: bool,
     pub cause: ObligationCause<'tcx>,
     pub variance_info: ty::VarianceDiagInfo<'tcx>,
+    pub outlives_constraint: OutlivesConstraint<'tcx>,
 }
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index 29195b3922f..9271a2f4dc7 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -25,7 +25,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     /// constraints should occur within this method so that those
     /// constraints can be properly localized!**
     #[instrument(skip(self, op), level = "trace")]
-    pub(super) fn fully_perform_op<R, Op>(
+    pub(super) fn fully_perform_op<R: fmt::Debug, Op>(
         &mut self,
         locations: Locations,
         category: ConstraintCategory<'tcx>,
@@ -39,6 +39,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
         let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx)?;
 
+        debug!(?output, ?constraints);
+
         if let Some(data) = constraints {
             self.push_region_constraints(locations, category, data);
         }
@@ -102,6 +104,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         );
     }
 
+    #[instrument(level = "debug", skip(self))]
     pub(super) fn normalize_and_prove_instantiated_predicates(
         &mut self,
         // Keep this parameter for now, in case we start using
@@ -116,8 +119,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             .zip(instantiated_predicates.spans.into_iter())
         {
             debug!(?predicate);
-            let predicate = self.normalize(predicate, locations);
-            self.prove_predicate(predicate, locations, ConstraintCategory::Predicate(span));
+            let category = ConstraintCategory::Predicate(span);
+            let predicate = self.normalize_with_category(predicate, locations, category);
+            self.prove_predicate(predicate, locations, category);
         }
     }
 
@@ -153,15 +157,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         })
     }
 
-    #[instrument(skip(self), level = "debug")]
     pub(super) fn normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
     where
         T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
     {
+        self.normalize_with_category(value, location, ConstraintCategory::Boring)
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    pub(super) fn normalize_with_category<T>(
+        &mut self,
+        value: T,
+        location: impl NormalizeLocation,
+        category: ConstraintCategory<'tcx>,
+    ) -> T
+    where
+        T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
+    {
         let param_env = self.param_env;
         self.fully_perform_op(
             location.to_locations(),
-            ConstraintCategory::Boring,
+            category,
             param_env.and(type_op::normalize::Normalize::new(value)),
         )
         .unwrap_or_else(|NoSolution| {
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index 9fab7ad914a..71eae0583cb 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -86,7 +86,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         }
     }
 
-    pub(super) fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) {
+    fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) {
         debug!("generate: constraints at: {:#?}", self.locations);
 
         // Extract out various useful fields we'll need below.
@@ -98,15 +98,18 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         // region constraints like `for<'a> 'a: 'b`. At some point
         // when we move to universes, we will, and this assertion
         // will start to fail.
-        let ty::OutlivesPredicate(k1, r2) = query_constraint.no_bound_vars().unwrap_or_else(|| {
-            bug!("query_constraint {:?} contained bound vars", query_constraint,);
-        });
+        let ty::OutlivesPredicate(k1, r2) =
+            query_constraint.0.no_bound_vars().unwrap_or_else(|| {
+                bug!("query_constraint {:?} contained bound vars", query_constraint,);
+            });
+
+        let constraint_category = query_constraint.1;
 
         match k1.unpack() {
             GenericArgKind::Lifetime(r1) => {
                 let r1_vid = self.to_region_vid(r1);
                 let r2_vid = self.to_region_vid(r2);
-                self.add_outlives(r1_vid, r2_vid);
+                self.add_outlives(r1_vid, r2_vid, constraint_category);
             }
 
             GenericArgKind::Type(t1) => {
@@ -121,7 +124,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
                     Some(implicit_region_bound),
                     param_env,
                 )
-                .type_must_outlive(origin, t1, r2);
+                .type_must_outlive(origin, t1, r2, constraint_category);
             }
 
             GenericArgKind::Const(_) => {
@@ -168,10 +171,19 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         }
     }
 
-    fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) {
+    fn add_outlives(
+        &mut self,
+        sup: ty::RegionVid,
+        sub: ty::RegionVid,
+        category: ConstraintCategory<'tcx>,
+    ) {
+        let category = match self.category {
+            ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation => category,
+            _ => self.category,
+        };
         self.constraints.outlives_constraints.push(OutlivesConstraint {
             locations: self.locations,
-            category: self.category,
+            category,
             span: self.span,
             sub,
             sup,
@@ -191,10 +203,11 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
         _origin: SubregionOrigin<'tcx>,
         a: ty::Region<'tcx>,
         b: ty::Region<'tcx>,
+        constraint_category: ConstraintCategory<'tcx>,
     ) {
         let b = self.to_region_vid(b);
         let a = self.to_region_vid(a);
-        self.add_outlives(b, a);
+        self.add_outlives(b, a, constraint_category);
     }
 
     fn push_verify(
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index fc0e95f30c9..3ad89cfe02f 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -311,6 +311,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
     }
 
     fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
+        debug!(?constant, ?location, "visit_constant");
+
         self.super_constant(constant, location);
         let ty = self.sanitize_type(constant, constant.literal.ty());
 
@@ -1810,6 +1812,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     }
 
     fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) {
+        debug!(?op, ?location, "check_operand");
+
         if let Operand::Constant(constant) = op {
             let maybe_uneval = match constant.literal {
                 ConstantKind::Ty(ct) => match ct.kind() {
@@ -2560,7 +2564,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 .enumerate()
                 .filter_map(|(idx, constraint)| {
                     let ty::OutlivesPredicate(k1, r2) =
-                        constraint.no_bound_vars().unwrap_or_else(|| {
+                        constraint.0.no_bound_vars().unwrap_or_else(|| {
                             bug!("query_constraint {:?} contained bound vars", constraint,);
                         });
 
diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl
index 2899b8304bc..65371a28591 100644
--- a/compiler/rustc_error_messages/locales/en-US/infer.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl
@@ -110,6 +110,7 @@ infer_relate_param_bound = ...so that the type `{$name}` will meet its required
 infer_relate_param_bound_2 = ...that is required by this bound
 infer_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied
 infer_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait
+infer_ascribe_user_type_prove_predicate = ...so that the where clause holds
 
 infer_nothing = {""}
 
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 64c759f73d4..56e83489879 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -22,6 +22,7 @@ use rustc_data_structures::captures::Captures;
 use rustc_index::vec::Idx;
 use rustc_index::vec::IndexVec;
 use rustc_middle::arena::ArenaAllocatable;
+use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::relate::TypeRelation;
@@ -129,7 +130,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
         let region_constraints = self.with_region_constraints(|region_constraints| {
             make_query_region_constraints(
                 tcx,
-                region_obligations.iter().map(|r_o| (r_o.sup_type, r_o.sub_region)),
+                region_obligations
+                    .iter()
+                    .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
                 region_constraints,
             )
         });
@@ -248,6 +251,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
         // the original values `v_o` that was canonicalized into a
         // variable...
 
+        let constraint_category = cause.to_constraint_category();
+
         for (index, original_value) in original_values.var_values.iter().enumerate() {
             // ...with the value `v_r` of that variable from the query.
             let result_value = query_response.substitute_projected(self.tcx, &result_subst, |v| {
@@ -263,12 +268,14 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
                 (GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => {
                     // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
                     if v_o != v_r {
-                        output_query_region_constraints
-                            .outlives
-                            .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)));
-                        output_query_region_constraints
-                            .outlives
-                            .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)));
+                        output_query_region_constraints.outlives.push((
+                            ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)),
+                            constraint_category,
+                        ));
+                        output_query_region_constraints.outlives.push((
+                            ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)),
+                            constraint_category,
+                        ));
                     }
                 }
 
@@ -314,7 +321,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
                 // Screen out `'a: 'a` cases -- we skip the binder here but
                 // only compare the inner values to one another, so they are still at
                 // consistent binding levels.
-                let ty::OutlivesPredicate(k1, r2) = r_c.skip_binder();
+                let ty::OutlivesPredicate(k1, r2) = r_c.0.skip_binder();
                 if k1 != r2.into() { Some(r_c) } else { None }
             }),
         );
@@ -559,7 +566,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
         cause: ObligationCause<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Obligation<'tcx, ty::Predicate<'tcx>> {
-        let ty::OutlivesPredicate(k1, r2) = predicate.skip_binder();
+        let ty::OutlivesPredicate(k1, r2) = predicate.0.skip_binder();
 
         let atom = match k1.unpack() {
             GenericArgKind::Lifetime(r1) => {
@@ -574,7 +581,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
                 span_bug!(cause.span, "unexpected const outlives {:?}", predicate);
             }
         };
-        let predicate = predicate.rebind(atom).to_predicate(self.tcx);
+        let predicate = predicate.0.rebind(atom).to_predicate(self.tcx);
 
         Obligation::new(cause, param_env, predicate)
     }
@@ -625,7 +632,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
 /// creates query region constraints.
 pub fn make_query_region_constraints<'tcx>(
     tcx: TyCtxt<'tcx>,
-    outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>)>,
+    outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>, ConstraintCategory<'tcx>)>,
     region_constraints: &RegionConstraintData<'tcx>,
 ) -> QueryRegionConstraints<'tcx> {
     let RegionConstraintData { constraints, verifys, givens, member_constraints } =
@@ -638,26 +645,31 @@ pub fn make_query_region_constraints<'tcx>(
 
     let outlives: Vec<_> = constraints
         .iter()
-        .map(|(k, _)| match *k {
-            // Swap regions because we are going from sub (<=) to outlives
-            // (>=).
-            Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate(
-                tcx.mk_region(ty::ReVar(v2)).into(),
-                tcx.mk_region(ty::ReVar(v1)),
-            ),
-            Constraint::VarSubReg(v1, r2) => {
-                ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1)))
-            }
-            Constraint::RegSubVar(r1, v2) => {
-                ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1)
-            }
-            Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1),
+        .map(|(k, origin)| {
+            // no bound vars in the code above
+            let constraint = ty::Binder::dummy(match *k {
+                // Swap regions because we are going from sub (<=) to outlives
+                // (>=).
+                Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate(
+                    tcx.mk_region(ty::ReVar(v2)).into(),
+                    tcx.mk_region(ty::ReVar(v1)),
+                ),
+                Constraint::VarSubReg(v1, r2) => {
+                    ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1)))
+                }
+                Constraint::RegSubVar(r1, v2) => {
+                    ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1)
+                }
+                Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1),
+            });
+            (constraint, origin.to_constraint_category())
         })
-        .map(ty::Binder::dummy) // no bound vars in the code above
         .chain(
             outlives_obligations
-                .map(|(ty, r)| ty::OutlivesPredicate(ty.into(), r))
-                .map(ty::Binder::dummy), // no bound vars in the code above
+                // no bound vars in the code above
+                .map(|(ty, r, constraint_category)| {
+                    (ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), r)), constraint_category)
+                }),
         )
         .collect();
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index cffdf56bb6d..adaa47c0140 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -77,6 +77,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             infer::CheckAssociatedTypeBounds { ref parent, .. } => {
                 self.note_region_origin(err, &parent);
             }
+            infer::AscribeUserTypeProvePredicate(span) => {
+                RegionOriginNote::Plain {
+                    span,
+                    msg: fluent::infer::ascribe_user_type_prove_predicate,
+                }
+                .add_to_diagnostic(err);
+            }
         }
     }
 
@@ -356,6 +363,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
                 err
             }
+            infer::AscribeUserTypeProvePredicate(span) => {
+                let mut err =
+                    struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "lifetime instantiated with ",
+                    sup,
+                    "",
+                    None,
+                );
+                note_and_explain_region(
+                    self.tcx,
+                    &mut err,
+                    "but lifetime must outlive ",
+                    sub,
+                    "",
+                    None,
+                );
+                err
+            }
         }
     }
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index bbbc044b85a..efcb6c92998 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -20,6 +20,7 @@ use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
 use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
 use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
+use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::traits::select;
 use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind};
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
@@ -408,7 +409,11 @@ pub enum SubregionOrigin<'tcx> {
 
     /// Comparing the signature and requirements of an impl method against
     /// the containing trait.
-    CompareImplItemObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId },
+    CompareImplItemObligation {
+        span: Span,
+        impl_item_def_id: LocalDefId,
+        trait_item_def_id: DefId,
+    },
 
     /// Checking that the bounds of a trait's associated type hold for a given impl
     CheckAssociatedTypeBounds {
@@ -416,12 +421,24 @@ pub enum SubregionOrigin<'tcx> {
         impl_item_def_id: LocalDefId,
         trait_item_def_id: DefId,
     },
+
+    AscribeUserTypeProvePredicate(Span),
 }
 
 // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 static_assert_size!(SubregionOrigin<'_>, 32);
 
+impl<'tcx> SubregionOrigin<'tcx> {
+    pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> {
+        match self {
+            Self::Subtype(type_trace) => type_trace.cause.to_constraint_category(),
+            Self::AscribeUserTypeProvePredicate(span) => ConstraintCategory::Predicate(*span),
+            _ => ConstraintCategory::BoringNoLocation,
+        }
+    }
+}
+
 /// Times when we replace late-bound regions with variables:
 #[derive(Clone, Copy, Debug)]
 pub enum LateBoundRegionConversionTime {
@@ -1991,6 +2008,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
             DataBorrowed(_, a) => a,
             ReferenceOutlivesReferent(_, a) => a,
             CompareImplItemObligation { span, .. } => span,
+            AscribeUserTypeProvePredicate(span) => span,
             CheckAssociatedTypeBounds { ref parent, .. } => parent.span(),
         }
     }
@@ -2023,6 +2041,10 @@ impl<'tcx> SubregionOrigin<'tcx> {
                 parent: Box::new(default()),
             },
 
+            traits::ObligationCauseCode::AscribeUserTypeProvePredicate(span) => {
+                SubregionOrigin::AscribeUserTypeProvePredicate(span)
+            }
+
             _ => default(),
         }
     }
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 74c8bd88d27..b65080e74c4 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -69,6 +69,7 @@ use crate::infer::{
 use crate::traits::{ObligationCause, ObligationCauseCode};
 use rustc_data_structures::undo_log::UndoLogs;
 use rustc_hir::def_id::LocalDefId;
+use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable};
 use smallvec::smallvec;
@@ -163,7 +164,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
 
             let outlives =
                 &mut TypeOutlives::new(self, self.tcx, &region_bound_pairs, None, param_env);
-            outlives.type_must_outlive(origin, sup_type, sub_region);
+            let category = origin.to_constraint_category();
+            outlives.type_must_outlive(origin, sup_type, sub_region, category);
         }
     }
 
@@ -207,6 +209,7 @@ pub trait TypeOutlivesDelegate<'tcx> {
         origin: SubregionOrigin<'tcx>,
         a: ty::Region<'tcx>,
         b: ty::Region<'tcx>,
+        constraint_category: ConstraintCategory<'tcx>,
     );
 
     fn push_verify(
@@ -255,12 +258,13 @@ where
         origin: infer::SubregionOrigin<'tcx>,
         ty: Ty<'tcx>,
         region: ty::Region<'tcx>,
+        category: ConstraintCategory<'tcx>,
     ) {
         assert!(!ty.has_escaping_bound_vars());
 
         let mut components = smallvec![];
         push_outlives_components(self.tcx, ty, &mut components);
-        self.components_must_outlive(origin, &components, region);
+        self.components_must_outlive(origin, &components, region, category);
     }
 
     fn components_must_outlive(
@@ -268,12 +272,13 @@ where
         origin: infer::SubregionOrigin<'tcx>,
         components: &[Component<'tcx>],
         region: ty::Region<'tcx>,
+        category: ConstraintCategory<'tcx>,
     ) {
         for component in components.iter() {
             let origin = origin.clone();
             match component {
                 Component::Region(region1) => {
-                    self.delegate.push_sub_region_constraint(origin, region, *region1);
+                    self.delegate.push_sub_region_constraint(origin, region, *region1, category);
                 }
                 Component::Param(param_ty) => {
                     self.param_ty_must_outlive(origin, region, *param_ty);
@@ -282,7 +287,7 @@ where
                     self.projection_must_outlive(origin, region, *projection_ty);
                 }
                 Component::EscapingProjection(subcomponents) => {
-                    self.components_must_outlive(origin, &subcomponents, region);
+                    self.components_must_outlive(origin, &subcomponents, region, category);
                 }
                 Component::UnresolvedInferenceVariable(v) => {
                     // ignore this, we presume it will yield an error
@@ -392,10 +397,20 @@ where
             for k in projection_ty.substs {
                 match k.unpack() {
                     GenericArgKind::Lifetime(lt) => {
-                        self.delegate.push_sub_region_constraint(origin.clone(), region, lt);
+                        self.delegate.push_sub_region_constraint(
+                            origin.clone(),
+                            region,
+                            lt,
+                            origin.to_constraint_category(),
+                        );
                     }
                     GenericArgKind::Type(ty) => {
-                        self.type_must_outlive(origin.clone(), ty, region);
+                        self.type_must_outlive(
+                            origin.clone(),
+                            ty,
+                            region,
+                            origin.to_constraint_category(),
+                        );
                     }
                     GenericArgKind::Const(_) => {
                         // Const parameters don't impose constraints.
@@ -433,7 +448,8 @@ where
             let unique_bound = trait_bounds[0];
             debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound);
             debug!("projection_must_outlive: unique declared bound appears in trait ref");
-            self.delegate.push_sub_region_constraint(origin, region, unique_bound);
+            let category = origin.to_constraint_category();
+            self.delegate.push_sub_region_constraint(origin, region, unique_bound, category);
             return;
         }
 
@@ -455,6 +471,7 @@ impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'tcx> {
         origin: SubregionOrigin<'tcx>,
         a: ty::Region<'tcx>,
         b: ty::Region<'tcx>,
+        _constraint_category: ConstraintCategory<'tcx>,
     ) {
         self.sub_regions(origin, a, b)
     }
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 200de9079c2..e467ca13c8e 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -22,6 +22,7 @@
 //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
 
 use crate::infer::MemberConstraint;
+use crate::mir::ConstraintCategory;
 use crate::ty::subst::GenericArg;
 use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt};
 use rustc_index::vec::IndexVec;
@@ -290,8 +291,10 @@ impl<'tcx, V> Canonical<'tcx, V> {
     }
 }
 
-pub type QueryOutlivesConstraint<'tcx> =
-    ty::Binder<'tcx, ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>>;
+pub type QueryOutlivesConstraint<'tcx> = (
+    ty::Binder<'tcx, ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>>,
+    ConstraintCategory<'tcx>,
+);
 
 TrivialTypeTraversalAndLiftImpls! {
     for <'tcx> {
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 594c14a642d..d89efe2b3f0 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -327,7 +327,7 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16);
 ///
 /// See also `rustc_const_eval::borrow_check::constraints`.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
-#[derive(TyEncodable, TyDecodable, HashStable)]
+#[derive(TyEncodable, TyDecodable, HashStable, Lift, TypeVisitable, TypeFoldable)]
 pub enum ConstraintCategory<'tcx> {
     Return(ReturnConstraint),
     Yield,
@@ -369,7 +369,7 @@ pub enum ConstraintCategory<'tcx> {
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
-#[derive(TyEncodable, TyDecodable, HashStable)]
+#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)]
 pub enum ReturnConstraint {
     Normal,
     ClosureUpvar(Field),
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index a95e6a61854..68a7af0b8c8 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -10,6 +10,7 @@ mod structural_impls;
 pub mod util;
 
 use crate::infer::canonical::Canonical;
+use crate::mir::ConstraintCategory;
 use crate::ty::abstract_const::NotConstEvaluatable;
 use crate::ty::subst::SubstsRef;
 use crate::ty::{self, AdtKind, Ty, TyCtxt};
@@ -183,6 +184,16 @@ impl<'tcx> ObligationCause<'tcx> {
             variant(DerivedObligationCause { parent_trait_pred, parent_code: self.code }).into();
         self
     }
+
+    pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> {
+        match self.code() {
+            MatchImpl(cause, _) => cause.to_constraint_category(),
+            AscribeUserTypeProvePredicate(predicate_span) => {
+                ConstraintCategory::Predicate(*predicate_span)
+            }
+            _ => ConstraintCategory::BoringNoLocation,
+        }
+    }
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
@@ -418,6 +429,8 @@ pub enum ObligationCauseCode<'tcx> {
         is_lit: bool,
         output_ty: Option<Ty<'tcx>>,
     },
+
+    AscribeUserTypeProvePredicate(Span),
 }
 
 /// The 'location' at which we try to perform HIR-based wf checking.
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index e6bd2eed565..37f88016f60 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -3,7 +3,7 @@
 //! hand, though we've recently added some macros and proc-macros to help with the tedium.
 
 use crate::mir::interpret;
-use crate::mir::ProjectionKind;
+use crate::mir::{Field, ProjectionKind};
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
 use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
 use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
@@ -648,6 +648,20 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> {
     }
 }
 
+impl<'tcx> Lift<'tcx> for Field {
+    type Lifted = Field;
+    fn lift_to_tcx(self, _tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
+        Some(self)
+    }
+}
+
+impl<'tcx> Lift<'tcx> for crate::mir::ReturnConstraint {
+    type Lifted = crate::mir::ReturnConstraint;
+    fn lift_to_tcx(self, _tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
+        Some(self)
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // TypeFoldable implementations.
 
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index cb605cacc9c..99c5ab6aacd 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -2256,7 +2256,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             | ObligationCauseCode::QuestionMark
             | ObligationCauseCode::CheckAssociatedTypeBounds { .. }
             | ObligationCauseCode::LetElse
-            | ObligationCauseCode::BinOp { .. } => {}
+            | ObligationCauseCode::BinOp { .. }
+            | ObligationCauseCode::AscribeUserTypeProvePredicate(..) => {}
             ObligationCauseCode::SliceOrArrayElem => {
                 err.note("slice and array elements must have `Sized` type");
             }
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index f65fc5bad0d..a3f8f4e2ed0 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -48,10 +48,11 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
         T: TypeFoldable<'tcx>,
     {
         debug!(
-            "normalize::<{}>(value={:?}, param_env={:?})",
+            "normalize::<{}>(value={:?}, param_env={:?}, cause={:?})",
             std::any::type_name::<T>(),
             value,
             self.param_env,
+            self.cause,
         );
         if !needs_normalization(&value, self.param_env.reveal()) {
             return Ok(Normalized { value, obligations: vec![] });
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
index f6e196e3141..18988861add 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -23,7 +23,7 @@ impl<F, G> CustomTypeOp<F, G> {
     }
 }
 
-impl<'tcx, F, R, G> super::TypeOp<'tcx> for CustomTypeOp<F, G>
+impl<'tcx, F, R: fmt::Debug, G> super::TypeOp<'tcx> for CustomTypeOp<F, G>
 where
     F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'tcx>) -> Fallible<InferOk<'tcx, R>>,
     G: Fn() -> String,
@@ -89,8 +89,8 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
         infcx.tcx,
         region_obligations
             .iter()
-            .map(|r_o| (r_o.sup_type, r_o.sub_region))
-            .map(|(ty, r)| (infcx.resolve_vars_if_possible(ty), r)),
+            .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category()))
+            .map(|(ty, r, cc)| (infcx.resolve_vars_if_possible(ty), r, cc)),
         &region_constraint_data,
     );
 
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 578e1d00cf9..8a79165702c 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -26,7 +26,7 @@ pub use rustc_middle::traits::query::type_op::*;
 /// extract out the resulting region constraints (or an error if it
 /// cannot be completed).
 pub trait TypeOp<'tcx>: Sized + fmt::Debug {
-    type Output;
+    type Output: fmt::Debug;
     type ErrorInfo;
 
     /// Processes the operation and all resulting obligations,
diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs
index 60e9b88107d..1a63f853211 100644
--- a/compiler/rustc_traits/src/type_op.rs
+++ b/compiler/rustc_traits/src/type_op.rs
@@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId;
 use rustc_infer::infer::at::ToTrace;
 use rustc_infer::infer::canonical::{Canonical, QueryResponse};
 use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt};
-use rustc_infer::traits::TraitEngineExt as _;
+use rustc_infer::traits::{ObligationCauseCode, TraitEngineExt as _};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts};
 use rustc_middle::ty::{
@@ -22,6 +22,7 @@ use rustc_trait_selection::traits::query::type_op::subtype::Subtype;
 use rustc_trait_selection::traits::query::{Fallible, NoSolution};
 use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, TraitEngine};
 use std::fmt;
+use std::iter::zip;
 
 pub(crate) fn provide(p: &mut Providers) {
     *p = Providers {
@@ -61,14 +62,15 @@ pub fn type_op_ascribe_user_type_with_span<'a, 'tcx: 'a>(
         mir_ty, def_id, user_substs
     );
 
-    let mut cx = AscribeUserTypeCx { infcx, param_env, fulfill_cx };
-    cx.relate_mir_and_user_ty(mir_ty, def_id, user_substs, span)?;
+    let mut cx = AscribeUserTypeCx { infcx, param_env, span: span.unwrap_or(DUMMY_SP), fulfill_cx };
+    cx.relate_mir_and_user_ty(mir_ty, def_id, user_substs)?;
     Ok(())
 }
 
 struct AscribeUserTypeCx<'me, 'tcx> {
     infcx: &'me InferCtxt<'me, 'tcx>,
     param_env: ParamEnv<'tcx>,
+    span: Span,
     fulfill_cx: &'me mut dyn TraitEngine<'tcx>,
 }
 
@@ -79,7 +81,7 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
     {
         self.infcx
             .partially_normalize_associated_types_in(
-                ObligationCause::misc(DUMMY_SP, hir::CRATE_HIR_ID),
+                ObligationCause::misc(self.span, hir::CRATE_HIR_ID),
                 self.param_env,
                 value,
             )
@@ -91,18 +93,13 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
         T: ToTrace<'tcx>,
     {
         self.infcx
-            .at(&ObligationCause::dummy(), self.param_env)
+            .at(&ObligationCause::dummy_with_span(self.span), self.param_env)
             .relate(a, variance, b)?
             .into_value_registering_obligations(self.infcx, self.fulfill_cx);
         Ok(())
     }
 
-    fn prove_predicate(&mut self, predicate: Predicate<'tcx>, span: Option<Span>) {
-        let cause = if let Some(span) = span {
-            ObligationCause::dummy_with_span(span)
-        } else {
-            ObligationCause::dummy()
-        };
+    fn prove_predicate(&mut self, predicate: Predicate<'tcx>, cause: ObligationCause<'tcx>) {
         self.fulfill_cx.register_predicate_obligation(
             self.infcx,
             Obligation::new(cause, self.param_env, predicate),
@@ -126,7 +123,6 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
         mir_ty: Ty<'tcx>,
         def_id: DefId,
         user_substs: UserSubsts<'tcx>,
-        span: Option<Span>,
     ) -> Result<(), NoSolution> {
         let UserSubsts { user_self_ty, substs } = user_substs;
         let tcx = self.tcx();
@@ -145,10 +141,20 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
         // outlives" error messages.
         let instantiated_predicates =
             self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs);
+
+        let cause = ObligationCause::dummy_with_span(self.span);
+
         debug!(?instantiated_predicates);
-        for instantiated_predicate in instantiated_predicates.predicates {
-            let instantiated_predicate = self.normalize(instantiated_predicate);
-            self.prove_predicate(instantiated_predicate, span);
+        for (instantiated_predicate, predicate_span) in
+            zip(instantiated_predicates.predicates, instantiated_predicates.spans)
+        {
+            let span = if self.span == DUMMY_SP { predicate_span } else { self.span };
+            let cause = ObligationCause::new(
+                span,
+                hir::CRATE_HIR_ID,
+                ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span),
+            );
+            self.prove_predicate(instantiated_predicate, cause);
         }
 
         if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
@@ -161,7 +167,7 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
             self.prove_predicate(
                 ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into()))
                     .to_predicate(self.tcx()),
-                span,
+                cause.clone(),
             );
         }
 
@@ -178,7 +184,7 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
         // which...could happen with normalization...
         self.prove_predicate(
             ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())).to_predicate(self.tcx()),
-            span,
+            cause,
         );
         Ok(())
     }
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index bc644c694a0..27b3da8ab3d 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -10,6 +10,7 @@ use rustc_hir::ItemKind;
 use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs};
 use rustc_infer::infer::outlives::obligations::TypeOutlives;
 use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
+use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
 use rustc_middle::ty::trait_def::TraitSpecializationKind;
@@ -663,7 +664,7 @@ fn ty_known_to_outlive<'tcx>(
     resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| {
         let origin = infer::RelateParamBound(DUMMY_SP, ty, None);
         let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env);
-        outlives.type_must_outlive(origin, ty, region);
+        outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation);
     })
 }
 
@@ -681,7 +682,12 @@ fn region_known_to_outlive<'tcx>(
         use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate;
         let origin = infer::RelateRegionParamBound(DUMMY_SP);
         // `region_a: region_b` -> `region_b <= region_a`
-        infcx.push_sub_region_constraint(origin, region_b, region_a);
+        infcx.push_sub_region_constraint(
+            origin,
+            region_b,
+            region_a,
+            ConstraintCategory::BoringNoLocation,
+        );
     })
 }
 
diff --git a/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.rs b/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.rs
new file mode 100644
index 00000000000..719d1bd5a4c
--- /dev/null
+++ b/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.rs
@@ -0,0 +1,35 @@
+// check-fail
+// known-bug
+
+// This gives us problems because `for<'a> I::Item<'a>: Debug` should mean "for
+// all 'a where I::Item<'a> is WF", but really means "for all 'a possible"
+
+use std::fmt::Debug;
+
+pub trait LendingIterator {
+    type Item<'this>
+    where
+        Self: 'this;
+}
+
+pub struct WindowsMut<'x> {
+    slice: &'x (),
+}
+
+impl<'y> LendingIterator for WindowsMut<'y> {
+    type Item<'this> = &'this mut () where 'y: 'this;
+}
+
+fn print_items<I>(_iter: I)
+where
+    I: LendingIterator,
+    for<'a> I::Item<'a>: Debug,
+{
+}
+
+fn main() {
+    let slice = &mut ();
+    //~^ temporary value dropped while borrowed
+    let windows = WindowsMut { slice };
+    print_items::<WindowsMut<'_>>(windows);
+}
diff --git a/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr b/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr
new file mode 100644
index 00000000000..414999881d4
--- /dev/null
+++ b/src/test/ui/generic-associated-types/bugs/hrtb-implied-1.stderr
@@ -0,0 +1,20 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/hrtb-implied-1.rs:31:22
+   |
+LL |     let slice = &mut ();
+   |                      ^^ creates a temporary which is freed while still in use
+...
+LL |     print_items::<WindowsMut<'_>>(windows);
+   |     -------------------------------------- argument requires that borrow lasts for `'static`
+LL | }
+   | - temporary value is freed at the end of this statement
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/hrtb-implied-1.rs:26:26
+   |
+LL |     for<'a> I::Item<'a>: Debug,
+   |                          ^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/src/test/ui/generic-associated-types/bugs/hrtb-implied-2.rs b/src/test/ui/generic-associated-types/bugs/hrtb-implied-2.rs
new file mode 100644
index 00000000000..8e6c5348e71
--- /dev/null
+++ b/src/test/ui/generic-associated-types/bugs/hrtb-implied-2.rs
@@ -0,0 +1,40 @@
+// check-fail
+// known-bug
+
+// This gives us problems because `for<'a> I::Item<'a>: Debug` should mean "for
+// all 'a where I::Item<'a> is WF", but really means "for all 'a possible"
+
+trait LendingIterator: Sized {
+    type Item<'a>
+    where
+        Self: 'a;
+    fn next(&mut self) -> Self::Item<'_>;
+}
+fn fails<I: LendingIterator, F>(iter: &mut I, f: F) -> bool
+where
+    F: FnMut(I::Item<'_>),
+{
+    let mut iter2 = Eat(iter, f);
+    let _next = iter2.next();
+    //~^ borrowed data escapes
+    true
+}
+impl<I: LendingIterator> LendingIterator for &mut I {
+    type Item<'a> = I::Item<'a> where Self:'a;
+    fn next(&mut self) -> Self::Item<'_> {
+        (**self).next()
+    }
+}
+
+struct Eat<I, F>(I, F);
+impl<I: LendingIterator, F> Iterator for Eat<I, F>
+where
+    F: FnMut(I::Item<'_>),
+{
+    type Item = ();
+    fn next(&mut self) -> Option<Self::Item> {
+        None
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/bugs/hrtb-implied-2.stderr b/src/test/ui/generic-associated-types/bugs/hrtb-implied-2.stderr
new file mode 100644
index 00000000000..1ee270398de
--- /dev/null
+++ b/src/test/ui/generic-associated-types/bugs/hrtb-implied-2.stderr
@@ -0,0 +1,22 @@
+error[E0521]: borrowed data escapes outside of function
+  --> $DIR/hrtb-implied-2.rs:18:17
+   |
+LL | fn fails<I: LendingIterator, F>(iter: &mut I, f: F) -> bool
+   |                                 ----  - let's call the lifetime of this reference `'1`
+   |                                 |
+   |                                 `iter` is a reference that is only valid in the function body
+...
+LL |     let _next = iter2.next();
+   |                 ^^^^^^^^^^^^
+   |                 |
+   |                 `iter` escapes the function body here
+   |                 argument requires that `'1` must outlive `'static`
+   |
+   = note: requirement occurs because of a mutable reference to `Eat<&mut I, F>`
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+   = note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0521`.
diff --git a/src/test/ui/generic-associated-types/bugs/hrtb-implied-3.rs b/src/test/ui/generic-associated-types/bugs/hrtb-implied-3.rs
new file mode 100644
index 00000000000..bc9e6c8aea8
--- /dev/null
+++ b/src/test/ui/generic-associated-types/bugs/hrtb-implied-3.rs
@@ -0,0 +1,23 @@
+trait LendingIterator {
+    type Item<'a>
+    where
+        Self: 'a;
+}
+
+impl LendingIterator for &str {
+    type Item<'a> = () where Self:'a;
+}
+
+fn trivial_bound<I>(_: I)
+where
+    I: LendingIterator,
+    for<'a> I::Item<'a>: Sized,
+{
+}
+
+fn fails(iter: &str) {
+    trivial_bound(iter);
+    //~^ borrowed data escapes
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/bugs/hrtb-implied-3.stderr b/src/test/ui/generic-associated-types/bugs/hrtb-implied-3.stderr
new file mode 100644
index 00000000000..c67e02437cd
--- /dev/null
+++ b/src/test/ui/generic-associated-types/bugs/hrtb-implied-3.stderr
@@ -0,0 +1,22 @@
+error[E0521]: borrowed data escapes outside of function
+  --> $DIR/hrtb-implied-3.rs:19:5
+   |
+LL | fn fails(iter: &str) {
+   |          ----  - let's call the lifetime of this reference `'1`
+   |          |
+   |          `iter` is a reference that is only valid in the function body
+LL |     trivial_bound(iter);
+   |     ^^^^^^^^^^^^^^^^^^^
+   |     |
+   |     `iter` escapes the function body here
+   |     argument requires that `'1` must outlive `'static`
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/hrtb-implied-3.rs:14:26
+   |
+LL |     for<'a> I::Item<'a>: Sized,
+   |                          ^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0521`.
diff --git a/src/test/ui/generic-associated-types/trait-objects.extended.stderr b/src/test/ui/generic-associated-types/trait-objects.extended.stderr
index 086177cc106..45b64d2b024 100644
--- a/src/test/ui/generic-associated-types/trait-objects.extended.stderr
+++ b/src/test/ui/generic-associated-types/trait-objects.extended.stderr
@@ -11,6 +11,8 @@ LL |     x.size_hint().0
    |     |
    |     `x` escapes the function body here
    |     argument requires that `'1` must outlive `'static`
+   |
+   = note: due to current limitations in the borrow checker, this implies a `'static` lifetime
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/higher-rank-trait-bounds/hrtb-just-for-static.stderr b/src/test/ui/higher-rank-trait-bounds/hrtb-just-for-static.stderr
index b4312091edb..31e11e12835 100644
--- a/src/test/ui/higher-rank-trait-bounds/hrtb-just-for-static.stderr
+++ b/src/test/ui/higher-rank-trait-bounds/hrtb-just-for-static.stderr
@@ -14,6 +14,12 @@ LL | fn give_some<'a>() {
    |              -- lifetime `'a` defined here
 LL |     want_hrtb::<&'a u32>()
    |     ^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/hrtb-just-for-static.rs:9:15
+   |
+LL |     where T : for<'a> Foo<&'a isize>
+   |               ^^^^^^^^^^^^^^^^^^^^^^
 
 error: implementation of `Foo` is not general enough
   --> $DIR/hrtb-just-for-static.rs:30:5
diff --git a/src/test/ui/higher-rank-trait-bounds/hrtb-perfect-forwarding.stderr b/src/test/ui/higher-rank-trait-bounds/hrtb-perfect-forwarding.stderr
index 1461e7fd2dd..5e75a4cc8af 100644
--- a/src/test/ui/higher-rank-trait-bounds/hrtb-perfect-forwarding.stderr
+++ b/src/test/ui/higher-rank-trait-bounds/hrtb-perfect-forwarding.stderr
@@ -46,6 +46,12 @@ LL | fn foo_hrtb_bar_not<'b, T>(mut t: T)
 ...
 LL |     foo_hrtb_bar_not(&mut t);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/hrtb-perfect-forwarding.rs:37:8
+   |
+LL |     T: for<'a> Foo<&'a isize> + Bar<&'b isize>,
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
 error: implementation of `Bar` is not general enough
   --> $DIR/hrtb-perfect-forwarding.rs:43:5
diff --git a/src/test/ui/issues/issue-26217.stderr b/src/test/ui/issues/issue-26217.stderr
index c7601caacdc..73c772205c3 100644
--- a/src/test/ui/issues/issue-26217.stderr
+++ b/src/test/ui/issues/issue-26217.stderr
@@ -5,6 +5,12 @@ LL | fn bar<'a>() {
    |        -- lifetime `'a` defined here
 LL |     foo::<&'a i32>();
    |     ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/issue-26217.rs:1:30
+   |
+LL | fn foo<T>() where for<'a> T: 'a {}
+   |                              ^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/local-outlives-static-via-hrtb.stderr b/src/test/ui/nll/local-outlives-static-via-hrtb.stderr
index 61009da49ff..f5c10f3ddea 100644
--- a/src/test/ui/nll/local-outlives-static-via-hrtb.stderr
+++ b/src/test/ui/nll/local-outlives-static-via-hrtb.stderr
@@ -9,6 +9,12 @@ LL |     assert_static_via_hrtb(&local);
 LL |     assert_static_via_hrtb_with_assoc_type(&&local);
 LL | }
    | - `local` dropped here while still borrowed
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/local-outlives-static-via-hrtb.rs:15:53
+   |
+LL | fn assert_static_via_hrtb<G>(_: G) where for<'a> G: Outlives<'a> {}
+   |                                                     ^^^^^^^^^^^^
 
 error[E0597]: `local` does not live long enough
   --> $DIR/local-outlives-static-via-hrtb.rs:25:45
@@ -20,6 +26,12 @@ LL |     assert_static_via_hrtb_with_assoc_type(&&local);
    |     argument requires that `local` is borrowed for `'static`
 LL | }
    | - `local` dropped here while still borrowed
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/local-outlives-static-via-hrtb.rs:19:20
+   |
+LL |     for<'a> &'a T: Reference<AssociatedType = &'a ()>,
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/nll/type-test-universe.stderr b/src/test/ui/nll/type-test-universe.stderr
index 242486c360a..31e17d64b8c 100644
--- a/src/test/ui/nll/type-test-universe.stderr
+++ b/src/test/ui/nll/type-test-universe.stderr
@@ -11,6 +11,12 @@ LL | fn test2<'a>() {
    |          -- lifetime `'a` defined here
 LL |     outlives_forall::<Value<'a>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/type-test-universe.rs:6:16
+   |
+LL |     for<'u> T: 'u,
+   |                ^^
 
 error: aborting due to 2 previous errors