From e2af9c03ef4cfb769e391968a5cfbb1afe6f01e2 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 29 Jun 2026 20:05:45 +0200 Subject: [PATCH] borrowck: Introduce BlameConstraint::to_obligation_cause_from_path() Before this commit, `BlameConstraint` is a bit hard to understand at first, because of its `ObligationCause` field. In practice, `BlameConstraint` is just a slimmed down version of an `OutlivesConstraint` from `Vec`. The artificial `ObligationCause` is only needed in one place. Rather than having that exception polute the lower level analysis, bubble it up as much as we can. That way, it becomes clearer what `BlameConstraint` actually is. For reference, `ObligationCause` was introdued by 93ab12eeaba2c, but the codebase looked quite different back then. --- .../src/diagnostics/explain_borrow.rs | 4 +- .../src/diagnostics/region_errors.rs | 14 +++--- .../rustc_borrowck/src/region_infer/mod.rs | 49 +++++++++++-------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index f6abafb874165..1e336f1e4509d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -585,11 +585,11 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { NllRegionVariableOrigin::FreeRegion, outlived_region, ); - let BlameConstraint { category, from_closure, cause, .. } = blame_constraint; + let BlameConstraint { category, from_closure, span, .. } = blame_constraint; let outlived_fr_name = self.give_region_a_name(outlived_region); - (category, from_closure, cause.span, outlived_fr_name, path) + (category, from_closure, span, outlived_fr_name, path) } /// 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 1166e1265dadb..a82cd5c74c330 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -414,8 +414,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }; // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. - let cause = - self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid).0.cause; + let (blame_constraint, path) = + self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid); + let cause = blame_constraint.to_obligation_cause_from_path(&path); // FIXME these methods should have better names, and also probably not be this generic. // FIXME note that we *throw away* the error element here! We probably want to @@ -448,17 +449,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr); - let BlameConstraint { category, cause, variance_info, .. } = blame_constraint; + let BlameConstraint { category, span, variance_info, .. } = blame_constraint; - debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info); + debug!("report_region_error: category={:?} {:?} {:?}", category, span, variance_info); // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.regioncx.to_error_region(fr), self.regioncx.to_error_region(outlived_fr)) { let infer_err = self.infcx.err_ctxt(); - let nice = - NiceRegionError::new_from_span(&infer_err, self.mir_def_id(), cause.span, o, f); + let nice = NiceRegionError::new_from_span(&infer_err, self.mir_def_id(), span, o, f); if let Some(diag) = nice.try_report_from_nll() { self.buffer_error(diag); return; @@ -475,7 +475,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { fr_is_local, outlived_fr_is_local, category ); - let errci = ErrorConstraintInfo { fr, outlived_fr, category, span: cause.span }; + let errci = ErrorConstraintInfo { fr, outlived_fr, category, span }; let mut diag = match (category, fr_is_local, outlived_fr_is_local) { (ConstraintCategory::SolverRegionConstraint(span), _, _) => { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5e56ae80ff5da..64aa92c2c8878 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1346,7 +1346,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject: ClosureOutlivesSubject::Region(fr_minus), outlived_free_region: fr_plus, - blame_span: blame_constraint.cause.span, + blame_span: blame_constraint.span, category: blame_constraint.category, }); } @@ -1652,24 +1652,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { .collect::>() ); - // 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(predicate_span) = constraint.category { - // We currently do not store the `DefId` in the `ConstraintCategory` - // for performances reasons. The error reporting code used by NLL only - // uses the span, so this doesn't cause any problems at the moment. - Some(ObligationCauseCode::WhereClause(CRATE_DEF_ID.to_def_id(), predicate_span)) - } else { - None - } - }) - .unwrap_or_else(|| ObligationCauseCode::Misc); - // When reporting an error, there is typically a chain of constraints leading from some // "source" region which must outlive some "target" region. // In most cases, we prefer to "blame" the constraints closer to the target -- @@ -1836,7 +1818,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let blame_constraint = BlameConstraint { category: best_constraint.category, from_closure: best_constraint.from_closure, - cause: ObligationCause::new(best_constraint.span, CRATE_DEF_ID, cause_code.clone()), + span: best_constraint.span, variance_info: best_constraint.variance_info, }; (blame_constraint, path) @@ -1917,6 +1899,31 @@ impl<'tcx> RegionInferenceContext<'tcx> { pub(crate) struct BlameConstraint<'tcx> { pub category: ConstraintCategory<'tcx>, pub from_closure: bool, - pub cause: ObligationCause<'tcx>, + pub span: Span, pub variance_info: ty::VarianceDiagInfo>, } + +impl<'tcx> BlameConstraint<'tcx> { + pub(crate) fn to_obligation_cause_from_path( + &self, + path: &[OutlivesConstraint<'tcx>], + ) -> ObligationCause<'tcx> { + // 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(predicate_span) = constraint.category { + // We currently do not store the `DefId` in the `ConstraintCategory` + // for performances reasons. The error reporting code used by NLL only + // uses the span, so this doesn't cause any problems at the moment. + Some(ObligationCauseCode::WhereClause(CRATE_DEF_ID.to_def_id(), predicate_span)) + } else { + None + } + }) + .unwrap_or_else(|| ObligationCauseCode::Misc); + + ObligationCause::new(self.span, CRATE_DEF_ID, cause_code.clone()) + } +}