about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2021-08-28 18:45:37 -0500
committerAaron Hill <aa1ronham@gmail.com>2021-09-27 10:23:45 -0500
commit93ab12eeaba2c0cb02d4c89d95ddbc5eb38e1448 (patch)
tree7dc4ebf6d9154cdc5f024f0cb4b2db899e934715
parent3e8f32e1c52ca493c862facb7a69e7c3f1f97a18 (diff)
downloadrust-93ab12eeaba2c0cb02d4c89d95ddbc5eb38e1448.tar.gz
rust-93ab12eeaba2c0cb02d4c89d95ddbc5eb38e1448.zip
Improve cause information for NLL higher-ranked errors
This PR has several interconnected pieces:

1. In some of the NLL region error code, we now pass
   around an `ObligationCause`, instead of just a plain `Span`.
   This gets forwarded into `fulfill_cx.register_predicate_obligation`
   during error reporting.
2. The general InferCtxt error reporting code is extended to
   handle `ObligationCauseCode::BindingObligation`
3. A new enum variant `ConstraintCategory::Predicate` is added.
   We try to avoid using this as the 'best blame constraint' - instead,
   we use it to enhance the `ObligationCause` of the `BlameConstraint`
   that we do end up choosing.

As a result, several NLL error messages now contain the same
"the lifetime requirement is introduced here" message as non-NLL
errors.

Having an `ObligationCause` available will likely prove useful
for future improvements to NLL error messages.
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs118
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs18
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs48
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs10
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs13
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs13
-rw-r--r--compiler/rustc_middle/src/mir/query.rs5
-rw-r--r--compiler/rustc_traits/src/lib.rs2
-rw-r--r--compiler/rustc_traits/src/type_op.rs11
-rw-r--r--src/test/ui/associated-types/higher-ranked-projection.bad.nll.stderr5
-rw-r--r--src/test/ui/generator/resume-arg-late-bound.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/issue-79187-2.nll.stderr15
-rw-r--r--src/test/ui/lifetimes/issue-79187.nll.stderr5
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr5
-rw-r--r--src/test/ui/mismatched_types/closure-mismatch.nll.stderr5
16 files changed, 197 insertions, 85 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index 76e779bfec6..366ade1a713 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -9,7 +9,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc_span::Span;
 use rustc_trait_selection::traits::query::type_op;
 use rustc_trait_selection::traits::{SelectionContext, TraitEngineExt as _};
-use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_span};
+use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause};
 
 use std::fmt;
 use std::rc::Rc;
@@ -45,13 +45,12 @@ impl UniverseInfo<'tcx> {
         mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
         placeholder: ty::PlaceholderRegion,
         error_element: RegionElement,
-        span: Span,
+        cause: ObligationCause<'tcx>,
     ) {
         match self.0 {
             UniverseInfoInner::RelateTys { expected, found } => {
-                let body_id = mbcx.infcx.tcx.hir().local_def_id_to_hir_id(mbcx.mir_def_id());
                 let err = mbcx.infcx.report_mismatched_types(
-                    &ObligationCause::misc(span, body_id),
+                    &cause,
                     expected,
                     found,
                     TypeError::RegionsPlaceholderMismatch,
@@ -59,7 +58,7 @@ impl UniverseInfo<'tcx> {
                 err.buffer(&mut mbcx.errors_buffer);
             }
             UniverseInfoInner::TypeOp(ref type_op_info) => {
-                type_op_info.report_error(mbcx, placeholder, error_element, span);
+                type_op_info.report_error(mbcx, placeholder, error_element, cause);
             }
             UniverseInfoInner::Other => {
                 // FIXME: This error message isn't great, but it doesn't show
@@ -68,7 +67,7 @@ impl UniverseInfo<'tcx> {
                 mbcx.infcx
                     .tcx
                     .sess
-                    .struct_span_err(span, "higher-ranked subtype error")
+                    .struct_span_err(cause.span, "higher-ranked subtype error")
                     .buffer(&mut mbcx.errors_buffer);
             }
         }
@@ -130,7 +129,7 @@ trait TypeOpInfo<'tcx> {
     fn nice_error(
         &self,
         tcx: TyCtxt<'tcx>,
-        span: Span,
+        cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
     ) -> Option<DiagnosticBuilder<'tcx>>;
@@ -140,7 +139,7 @@ trait TypeOpInfo<'tcx> {
         mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
         placeholder: ty::PlaceholderRegion,
         error_element: RegionElement,
-        span: Span,
+        cause: ObligationCause<'tcx>,
     ) {
         let tcx = mbcx.infcx.tcx;
         let base_universe = self.base_universe();
@@ -150,7 +149,7 @@ trait TypeOpInfo<'tcx> {
         {
             adjusted
         } else {
-            self.fallback_error(tcx, span).buffer(&mut mbcx.errors_buffer);
+            self.fallback_error(tcx, cause.span).buffer(&mut mbcx.errors_buffer);
             return;
         };
 
@@ -175,7 +174,8 @@ trait TypeOpInfo<'tcx> {
 
         debug!(?placeholder_region);
 
-        let nice_error = self.nice_error(tcx, span, placeholder_region, error_region);
+        let span = cause.span;
+        let nice_error = self.nice_error(tcx, cause, placeholder_region, error_region);
 
         if let Some(nice_error) = nice_error {
             nice_error.buffer(&mut mbcx.errors_buffer);
@@ -205,15 +205,24 @@ impl TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
     fn nice_error(
         &self,
         tcx: TyCtxt<'tcx>,
-        span: Span,
+        cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
     ) -> Option<DiagnosticBuilder<'tcx>> {
-        tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
-            let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
-            type_op_prove_predicate_with_span(infcx, &mut *fulfill_cx, key, Some(span));
-            try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
-        })
+        tcx.infer_ctxt().enter_with_canonical(
+            cause.span,
+            &self.canonical_query,
+            |ref infcx, key, _| {
+                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
+                type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause);
+                try_extract_error_from_fulfill_cx(
+                    fulfill_cx,
+                    infcx,
+                    placeholder_region,
+                    error_region,
+                )
+            },
+        )
     }
 }
 
@@ -239,32 +248,41 @@ where
     fn nice_error(
         &self,
         tcx: TyCtxt<'tcx>,
-        span: Span,
+        cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
     ) -> Option<DiagnosticBuilder<'tcx>> {
-        tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
-            let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
-
-            let mut selcx = SelectionContext::new(infcx);
-
-            // FIXME(lqd): Unify and de-duplicate the following with the actual
-            // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
-            // `ObligationCause`. The normalization results are currently different between
-            // `AtExt::normalize` used in the query and `normalize` called below: the former fails
-            // to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check
-            // after #85499 lands to see if its fixes have erased this difference.
-            let (param_env, value) = key.into_parts();
-            let Normalized { value: _, obligations } = rustc_trait_selection::traits::normalize(
-                &mut selcx,
-                param_env,
-                ObligationCause::dummy_with_span(span),
-                value.value,
-            );
-            fulfill_cx.register_predicate_obligations(infcx, obligations);
-
-            try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
-        })
+        tcx.infer_ctxt().enter_with_canonical(
+            cause.span,
+            &self.canonical_query,
+            |ref infcx, key, _| {
+                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
+
+                let mut selcx = SelectionContext::new(infcx);
+
+                // FIXME(lqd): Unify and de-duplicate the following with the actual
+                // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
+                // `ObligationCause`. The normalization results are currently different between
+                // `AtExt::normalize` used in the query and `normalize` called below: the former fails
+                // to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check
+                // after #85499 lands to see if its fixes have erased this difference.
+                let (param_env, value) = key.into_parts();
+                let Normalized { value: _, obligations } = rustc_trait_selection::traits::normalize(
+                    &mut selcx,
+                    param_env,
+                    cause,
+                    value.value,
+                );
+                fulfill_cx.register_predicate_obligations(infcx, obligations);
+
+                try_extract_error_from_fulfill_cx(
+                    fulfill_cx,
+                    infcx,
+                    placeholder_region,
+                    error_region,
+                )
+            },
+        )
     }
 }
 
@@ -287,15 +305,25 @@ impl TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
     fn nice_error(
         &self,
         tcx: TyCtxt<'tcx>,
-        span: Span,
+        cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
     ) -> Option<DiagnosticBuilder<'tcx>> {
-        tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
-            let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
-            type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(span)).ok()?;
-            try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
-        })
+        tcx.infer_ctxt().enter_with_canonical(
+            cause.span,
+            &self.canonical_query,
+            |ref infcx, key, _| {
+                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
+                type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span))
+                    .ok()?;
+                try_extract_error_from_fulfill_cx(
+                    fulfill_cx,
+                    infcx,
+                    placeholder_region,
+                    error_region,
+                )
+            },
+        )
     }
 }
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 2d12a682e7a..d5de0801ac4 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -300,7 +300,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         borrow_region: RegionVid,
         outlived_region: RegionVid,
     ) -> (ConstraintCategory, bool, Span, Option<RegionName>) {
-        let BlameConstraint { category, from_closure, span, variance_info: _ } =
+        let BlameConstraint { category, from_closure, cause, variance_info: _ } =
             self.regioncx.best_blame_constraint(
                 &self.body,
                 borrow_region,
@@ -310,7 +310,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         let outlived_fr_name = self.give_region_a_name(outlived_region);
 
-        (category, from_closure, span, outlived_fr_name)
+        (category, from_closure, cause.span, outlived_fr_name)
     }
 
     /// Returns structured explanation for *why* the borrow contains the
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 0761d63c665..b127330efa2 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -13,6 +13,7 @@ use rustc_span::{BytePos, Span};
 
 use crate::borrowck_errors;
 
+use super::{OutlivesSuggestionBuilder, RegionName};
 use crate::region_infer::BlameConstraint;
 use crate::{
     nll::ConstraintDescription,
@@ -21,8 +22,6 @@ use crate::{
     MirBorrowckCtxt,
 };
 
-use super::{OutlivesSuggestionBuilder, RegionName};
-
 impl ConstraintDescription for ConstraintCategory {
     fn description(&self) -> &'static str {
         // Must end with a space. Allows for empty names to be provided.
@@ -41,7 +40,8 @@ impl ConstraintDescription for ConstraintCategory {
             ConstraintCategory::OpaqueType => "opaque type ",
             ConstraintCategory::ClosureUpvar(_) => "closure capture ",
             ConstraintCategory::Usage => "this usage ",
-            ConstraintCategory::Boring
+            ConstraintCategory::Predicate(_, _)
+            | ConstraintCategory::Boring
             | ConstraintCategory::BoringNoLocation
             | ConstraintCategory::Internal => "",
         }
@@ -217,7 +217,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
 
                     // Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
-                    let (_, span) = self.regioncx.find_outlives_blame_span(
+                    let (_, cause) = self.regioncx.find_outlives_blame_span(
                         &self.body,
                         longer_fr,
                         NllRegionVariableOrigin::Placeholder(placeholder),
@@ -227,7 +227,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     let universe = placeholder.universe;
                     let universe_info = self.regioncx.universe_info(universe);
 
-                    universe_info.report_error(self, placeholder, error_element, span);
+                    universe_info.report_error(self, placeholder, error_element, cause);
                 }
 
                 RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
@@ -275,15 +275,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     ) {
         debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
 
-        let BlameConstraint { category, span, variance_info, from_closure: _ } =
+        let BlameConstraint { category, cause, variance_info, from_closure: _ } =
             self.regioncx.best_blame_constraint(&self.body, fr, fr_origin, |r| {
                 self.regioncx.provides_universal_region(r, fr, outlived_fr)
             });
 
-        debug!("report_region_error: category={:?} {:?} {:?}", category, span, variance_info);
+        debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
         // Check if we can use one of the "nice region errors".
         if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
-            let nice = NiceRegionError::new_from_span(self.infcx, span, o, f);
+            let nice = NiceRegionError::new_from_span(self.infcx, cause.span, o, f);
             if let Some(diag) = nice.try_report_from_nll() {
                 diag.buffer(&mut self.errors_buffer);
                 return;
@@ -306,7 +306,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             fr_is_local,
             outlived_fr_is_local,
             category,
-            span,
+            span: cause.span,
         };
 
         let mut diag = match (category, fr_is_local, outlived_fr_is_local) {
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 128faab8d72..cb1b0e0c934 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -6,6 +6,7 @@ use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::graph::scc::Sccs;
 use rustc_hir::def_id::DefId;
+use rustc_hir::CRATE_HIR_ID;
 use rustc_index::vec::IndexVec;
 use rustc_infer::infer::canonical::QueryOutlivesConstraint;
 use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound};
@@ -14,6 +15,8 @@ use rustc_middle::mir::{
     Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
     ConstraintCategory, Local, Location, ReturnConstraint,
 };
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable};
 use rustc_span::Span;
 
@@ -1596,7 +1599,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     propagated_outlives_requirements.push(ClosureOutlivesRequirement {
                         subject: ClosureOutlivesSubject::Region(fr_minus),
                         outlived_free_region: fr,
-                        blame_span: blame_span_category.1,
+                        blame_span: blame_span_category.1.span,
                         category: blame_span_category.0,
                     });
                 }
@@ -1738,7 +1741,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 return BlameConstraint {
                     category: constraint.category,
                     from_closure: false,
-                    span,
+                    cause: ObligationCause::dummy_with_span(span),
                     variance_info: constraint.variance_info,
                 };
             }
@@ -1751,30 +1754,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             .map(|&(category, span)| BlameConstraint {
                 category,
                 from_closure: true,
-                span: span,
+                cause: ObligationCause::dummy_with_span(span),
                 variance_info: constraint.variance_info,
             })
             .unwrap_or(BlameConstraint {
                 category: constraint.category,
                 from_closure: false,
-                span: body.source_info(loc).span,
+                cause: ObligationCause::dummy_with_span(body.source_info(loc).span),
                 variance_info: constraint.variance_info,
             })
     }
 
-    /// Finds a good span to blame for the fact that `fr1` outlives `fr2`.
+    /// Finds a good `ObligationCause` to blame for the fact that `fr1` outlives `fr2`.
     crate fn find_outlives_blame_span(
         &self,
         body: &Body<'tcx>,
         fr1: RegionVid,
         fr1_origin: NllRegionVariableOrigin,
         fr2: RegionVid,
-    ) -> (ConstraintCategory, Span) {
-        let BlameConstraint { category, span, .. } =
+    ) -> (ConstraintCategory, ObligationCause<'tcx>) {
+        let BlameConstraint { category, cause, .. } =
             self.best_blame_constraint(body, fr1, fr1_origin, |r| {
                 self.provides_universal_region(r, fr1, fr2)
             });
-        (category, span)
+        (category, cause)
     }
 
     /// Walks the graph of constraints (where `'a: 'b` is considered
@@ -1990,6 +1993,21 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 .collect::<Vec<_>>()
         );
 
+        // 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`
+        // constraints. Currently, we just pick the first one.
+        let cause_code = path
+            .iter()
+            .find_map(|constraint| {
+                if let ConstraintCategory::Predicate(def_id, predicate_span) = constraint.category {
+                    Some(ObligationCauseCode::BindingObligation(def_id, predicate_span))
+                } else {
+                    None
+                }
+            })
+            .unwrap_or_else(|| ObligationCauseCode::MiscObligation);
+
         // Classify each of the constraints along the path.
         let mut categorized_path: Vec<BlameConstraint<'tcx>> = path
             .iter()
@@ -2000,7 +2018,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     BlameConstraint {
                         category: constraint.category,
                         from_closure: false,
-                        span: constraint.locations.span(body),
+                        cause: ObligationCause::new(
+                            constraint.locations.span(body),
+                            CRATE_HIR_ID,
+                            cause_code.clone(),
+                        ),
                         variance_info: constraint.variance_info,
                     }
                 }
@@ -2083,7 +2105,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     ConstraintCategory::OpaqueType
                     | ConstraintCategory::Boring
                     | ConstraintCategory::BoringNoLocation
-                    | ConstraintCategory::Internal => false,
+                    | ConstraintCategory::Internal
+                    | ConstraintCategory::Predicate(_, _) => false,
                     ConstraintCategory::TypeAnnotation
                     | ConstraintCategory::Return(_)
                     | ConstraintCategory::Yield => true,
@@ -2094,7 +2117,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     ConstraintCategory::OpaqueType
                     | ConstraintCategory::Boring
                     | ConstraintCategory::BoringNoLocation
-                    | ConstraintCategory::Internal => false,
+                    | ConstraintCategory::Internal
+                    | ConstraintCategory::Predicate(_, _) => false,
                     _ => true,
                 }
             }
@@ -2249,6 +2273,6 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
 pub struct BlameConstraint<'tcx> {
     pub category: ConstraintCategory,
     pub from_closure: bool,
-    pub span: Span,
+    pub cause: ObligationCause<'tcx>,
     pub variance_info: ty::VarianceDiagInfo<'tcx>,
 }
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index e7761988222..7ff74e4b96e 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -4,6 +4,7 @@ use rustc_infer::infer::canonical::Canonical;
 use rustc_infer::traits::query::NoSolution;
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::{self, ToPredicate, TypeFoldable};
+use rustc_span::def_id::DefId;
 use rustc_span::Span;
 use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
 use rustc_trait_selection::traits::query::Fallible;
@@ -100,12 +101,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
     pub(super) fn normalize_and_prove_instantiated_predicates(
         &mut self,
+        def_id: DefId,
         instantiated_predicates: ty::InstantiatedPredicates<'tcx>,
         locations: Locations,
     ) {
-        for predicate in instantiated_predicates.predicates {
+        for (predicate, span) in instantiated_predicates
+            .predicates
+            .into_iter()
+            .zip(instantiated_predicates.spans.into_iter())
+        {
             let predicate = self.normalize(predicate, locations);
-            self.prove_predicate(predicate, locations, ConstraintCategory::Boring);
+            self.prove_predicate(predicate, locations, ConstraintCategory::Predicate(def_id, span));
         }
     }
 
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index cf50fa38687..4bbeed39e45 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -32,6 +32,7 @@ use rustc_middle::ty::{
     self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, OpaqueTypeKey, RegionVid,
     ToPredicate, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, WithConstness,
 };
+use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::VariantIdx;
 use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -449,6 +450,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
             if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() {
                 let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
                 self.cx.normalize_and_prove_instantiated_predicates(
+                    def_id,
                     instantiated_predicates,
                     location.to_locations(),
                 );
@@ -2572,9 +2574,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             aggregate_kind, location
         );
 
-        let instantiated_predicates = match aggregate_kind {
+        let (def_id, instantiated_predicates) = match aggregate_kind {
             AggregateKind::Adt(def, _, substs, _, _) => {
-                tcx.predicates_of(def.did).instantiate(tcx, substs)
+                (def.did, tcx.predicates_of(def.did).instantiate(tcx, substs))
             }
 
             // For closures, we have some **extra requirements** we
@@ -2599,13 +2601,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             // clauses on the struct.
             AggregateKind::Closure(def_id, substs)
             | AggregateKind::Generator(def_id, substs, _) => {
-                self.prove_closure_bounds(tcx, def_id.expect_local(), substs, location)
+                (*def_id, self.prove_closure_bounds(tcx, def_id.expect_local(), substs, location))
             }
 
-            AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(),
+            AggregateKind::Array(_) | AggregateKind::Tuple => {
+                (CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty())
+            }
         };
 
         self.normalize_and_prove_instantiated_predicates(
+            def_id,
             instantiated_predicates,
             location.to_locations(),
         );
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index d9b7022f03a..45dd8868d6c 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -609,6 +609,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         err: &mut DiagnosticBuilder<'tcx>,
         cause: &ObligationCause<'tcx>,
         exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
+        terr: &TypeError<'tcx>,
     ) {
         match cause.code {
             ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
@@ -785,7 +786,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 err.help("try adding a diverging expression, such as `return` or `panic!(..)`");
                 err.help("...or use `match` instead of `let...else`");
             }
-            _ => (),
+            _ => {
+                if let ObligationCauseCode::BindingObligation(_, binding_span) =
+                    cause.code.peel_derives()
+                {
+                    if matches!(terr, TypeError::RegionsPlaceholderMismatch) {
+                        err.span_note(*binding_span, "the lifetime requirement is introduced here");
+                    }
+                }
+            }
         }
     }
 
@@ -1724,7 +1733,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
         // It reads better to have the error origin as the final
         // thing.
-        self.note_error_origin(diag, cause, exp_found);
+        self.note_error_origin(diag, cause, exp_found, terr);
     }
 
     pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index b003a504691..8d206d3eb1d 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -338,6 +338,11 @@ pub enum ConstraintCategory {
     OpaqueType,
     ClosureUpvar(hir::HirId),
 
+    /// A constraint from a user-written predicate
+    /// with the provided span, written on the item
+    /// with the given `DefId`
+    Predicate(DefId, Span),
+
     /// A "boring" constraint (caused by the given location) is one that
     /// the user probably doesn't want to see described in diagnostics,
     /// because it is kind of an artifact of the type system setup.
diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs
index 48c46c30693..8612499623b 100644
--- a/compiler/rustc_traits/src/lib.rs
+++ b/compiler/rustc_traits/src/lib.rs
@@ -19,7 +19,7 @@ mod normalize_erasing_regions;
 mod normalize_projection_ty;
 mod type_op;
 
-pub use type_op::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_span};
+pub use type_op::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause};
 
 use rustc_middle::ty::query::Providers;
 
diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs
index f954cab240c..cc0b7d5817b 100644
--- a/compiler/rustc_traits/src/type_op.rs
+++ b/compiler/rustc_traits/src/type_op.rs
@@ -257,7 +257,7 @@ fn type_op_prove_predicate<'tcx>(
     canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>,
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> {
     tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| {
-        type_op_prove_predicate_with_span(infcx, fulfill_cx, key, None);
+        type_op_prove_predicate_with_cause(infcx, fulfill_cx, key, ObligationCause::dummy());
         Ok(())
     })
 }
@@ -265,17 +265,12 @@ fn type_op_prove_predicate<'tcx>(
 /// The core of the `type_op_prove_predicate` query: for diagnostics purposes in NLL HRTB errors,
 /// this query can be re-run to better track the span of the obligation cause, and improve the error
 /// message. Do not call directly unless you're in that very specific context.
-pub fn type_op_prove_predicate_with_span<'a, 'tcx: 'a>(
+pub fn type_op_prove_predicate_with_cause<'a, 'tcx: 'a>(
     infcx: &'a InferCtxt<'a, 'tcx>,
     fulfill_cx: &'a mut dyn TraitEngine<'tcx>,
     key: ParamEnvAnd<'tcx, ProvePredicate<'tcx>>,
-    span: Option<Span>,
+    cause: ObligationCause<'tcx>,
 ) {
-    let cause = if let Some(span) = span {
-        ObligationCause::dummy_with_span(span)
-    } else {
-        ObligationCause::dummy()
-    };
     let (param_env, ProvePredicate { predicate }) = key.into_parts();
     fulfill_cx.register_predicate_obligation(infcx, Obligation::new(cause, param_env, predicate));
 }
diff --git a/src/test/ui/associated-types/higher-ranked-projection.bad.nll.stderr b/src/test/ui/associated-types/higher-ranked-projection.bad.nll.stderr
index e3bd0c2276e..de254b7a163 100644
--- a/src/test/ui/associated-types/higher-ranked-projection.bad.nll.stderr
+++ b/src/test/ui/associated-types/higher-ranked-projection.bad.nll.stderr
@@ -6,6 +6,11 @@ LL |     foo(());
    |
    = note: expected reference `&'a ()`
               found reference `&()`
+note: the lifetime requirement is introduced here
+  --> $DIR/higher-ranked-projection.rs:15:33
+   |
+LL |     where for<'a> &'a T: Mirror<Image=U>
+   |                                 ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generator/resume-arg-late-bound.nll.stderr b/src/test/ui/generator/resume-arg-late-bound.nll.stderr
index 25bc6afc550..b5144c607a8 100644
--- a/src/test/ui/generator/resume-arg-late-bound.nll.stderr
+++ b/src/test/ui/generator/resume-arg-late-bound.nll.stderr
@@ -6,6 +6,11 @@ LL |     test(gen);
    |
    = note: expected type `for<'a> Generator<&'a mut bool>`
               found type `Generator<&mut bool>`
+note: the lifetime requirement is introduced here
+  --> $DIR/resume-arg-late-bound.rs:8:17
+   |
+LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {}
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetimes/issue-79187-2.nll.stderr b/src/test/ui/lifetimes/issue-79187-2.nll.stderr
index 907b43d6762..04d9b64d64f 100644
--- a/src/test/ui/lifetimes/issue-79187-2.nll.stderr
+++ b/src/test/ui/lifetimes/issue-79187-2.nll.stderr
@@ -38,6 +38,11 @@ note: this closure does not fulfill the lifetime requirements
    |
 LL |     take_foo(|a| a);
    |              ^^^^^
+note: the lifetime requirement is introduced here
+  --> $DIR/issue-79187-2.rs:5:21
+   |
+LL | fn take_foo(_: impl Foo) {}
+   |                     ^^^
 
 error[E0308]: mismatched types
   --> $DIR/issue-79187-2.rs:9:5
@@ -47,6 +52,11 @@ LL |     take_foo(|a: &i32| a);
    |
    = note: expected reference `&i32`
               found reference `&i32`
+note: the lifetime requirement is introduced here
+  --> $DIR/issue-79187-2.rs:5:21
+   |
+LL | fn take_foo(_: impl Foo) {}
+   |                     ^^^
 
 error[E0308]: mismatched types
   --> $DIR/issue-79187-2.rs:10:5
@@ -56,6 +66,11 @@ LL |     take_foo(|a: &i32| -> &i32 { a });
    |
    = note: expected reference `&i32`
               found reference `&i32`
+note: the lifetime requirement is introduced here
+  --> $DIR/issue-79187-2.rs:5:21
+   |
+LL | fn take_foo(_: impl Foo) {}
+   |                     ^^^
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/lifetimes/issue-79187.nll.stderr b/src/test/ui/lifetimes/issue-79187.nll.stderr
index 725b132e83a..3a993e88d8a 100644
--- a/src/test/ui/lifetimes/issue-79187.nll.stderr
+++ b/src/test/ui/lifetimes/issue-79187.nll.stderr
@@ -11,6 +11,11 @@ note: this closure does not fulfill the lifetime requirements
    |
 LL |     let f = |_| ();
    |             ^^^^^^
+note: the lifetime requirement is introduced here
+  --> $DIR/issue-79187.rs:1:18
+   |
+LL | fn thing(x: impl FnOnce(&u32)) {}
+   |                  ^^^^^^^^^^^^
 
 error: implementation of `FnOnce` is not general enough
   --> $DIR/issue-79187.rs:5:5
diff --git a/src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr
index 5a1294f948f..5509226cb1c 100644
--- a/src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/issue_74400.nll.stderr
@@ -14,6 +14,11 @@ LL |     f(data, identity)
    |
    = note: expected type `for<'r> Fn<(&'r T,)>`
               found type `Fn<(&T,)>`
+note: the lifetime requirement is introduced here
+  --> $DIR/issue_74400.rs:8:34
+   |
+LL | fn f<T, S>(data: &[T], key: impl Fn(&T) -> S) {
+   |                                  ^^^^^^^^^^^
 
 error: implementation of `FnOnce` is not general enough
   --> $DIR/issue_74400.rs:12:5
diff --git a/src/test/ui/mismatched_types/closure-mismatch.nll.stderr b/src/test/ui/mismatched_types/closure-mismatch.nll.stderr
index f29126e6afc..bd36fab9288 100644
--- a/src/test/ui/mismatched_types/closure-mismatch.nll.stderr
+++ b/src/test/ui/mismatched_types/closure-mismatch.nll.stderr
@@ -20,6 +20,11 @@ note: this closure does not fulfill the lifetime requirements
    |
 LL |     baz(|_| ());
    |         ^^^^^^
+note: the lifetime requirement is introduced here
+  --> $DIR/closure-mismatch.rs:5:11
+   |
+LL | fn baz<T: Foo>(_: T) {}
+   |           ^^^
 
 error: aborting due to 2 previous errors