From ae16c9e369f79f983fab88d1660fa64ba0d1b1ac Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 29 Jun 2026 20:05:45 +0200 Subject: [PATCH 1/4] borrowck: Introduce BlameConstraint::to_obligation_cause_from_path() For reference: * 93ab12eeaba2c --- .../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()) + } +} From 8283d83cfe1c93395c70c6d6a0cd09cf6e99fcce Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sat, 27 Jun 2026 08:45:43 +0200 Subject: [PATCH 2/4] borrowck: Adjust best_blame_constraint() path item instead of creating new one So that we in an upcoming commit can return the index into path instead of a "duplicate". This passes: ./x test --bless tests/ui --keep-stage-std 1 --set rust.deny-warnings=false --- compiler/rustc_borrowck/src/region_infer/mod.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 64aa92c2c8878..505aa74b04437 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1630,7 +1630,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); // Edge case: it's possible that `'from_region` is an unnameable placeholder. - let path = if let Some(unnameable) = due_to_placeholder_outlives + let mut path = if let Some(unnameable) = due_to_placeholder_outlives && unnameable != from_region { // We ignore the extra edges due to unnameable placeholders to get @@ -1799,10 +1799,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None } }) { - OutlivesConstraint { - category: ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)), - ..path[best_choice] - } + path[best_choice].category = + ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); + path[best_choice] } else { path[best_choice] }; From 5d31676b1031fafc119b316f9e73a23b1b2a732a Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 30 Jun 2026 18:46:22 +0200 Subject: [PATCH 3/4] borrowck: Inline free_region_constraint_info() to simplify the code --- .../src/diagnostics/explain_borrow.rs | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 1e336f1e4509d..15e3cf28aac32 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -12,7 +12,7 @@ use rustc_middle::mir::{ Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{DesugaringKind, Span, kw, sym}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::error_reporting::traits::call_kind::CallKind; @@ -574,24 +574,6 @@ fn suggest_rewrite_if_let( } impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { - fn free_region_constraint_info( - &self, - borrow_region: RegionVid, - outlived_region: RegionVid, - ) -> (ConstraintCategory<'tcx>, bool, Span, Option, Vec>) - { - let (blame_constraint, path) = self.regioncx.best_blame_constraint( - borrow_region, - NllRegionVariableOrigin::FreeRegion, - outlived_region, - ); - let BlameConstraint { category, from_closure, span, .. } = blame_constraint; - - let outlived_fr_name = self.give_region_a_name(outlived_region); - - (category, from_closure, span, outlived_fr_name, path) - } - /// Returns structured explanation for *why* the borrow contains the /// point from `location`. This is key for the "3-point errors" /// [described in the NLL RFC][d]. @@ -707,9 +689,14 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // Here, under NLL: no cause was found. Under polonius: no cause was found, or a // boring local was found, which we ignore like NLLs do to match its diagnostics. if let Some(region) = self.regioncx.to_error_region_vid(borrow_region_vid) { - let (category, from_closure, span, region_name, path) = - self.free_region_constraint_info(borrow_region_vid, region); - if let Some(region_name) = region_name { + let (blame_constraint, path) = self.regioncx.best_blame_constraint( + borrow_region_vid, + NllRegionVariableOrigin::FreeRegion, + region, + ); + let BlameConstraint { category, from_closure, span, .. } = blame_constraint; + + if let Some(region_name) = self.give_region_a_name(region) { let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref()); BorrowExplanation::MustBeValidFor { category, From cab90926f8457e4a8e6fd507cf404a37a0fa2f4f Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 30 Jun 2026 18:55:53 +0200 Subject: [PATCH 4/4] borrowck: Represent 'best blame constraint' as index into `Vec` This makes the code simpler and easier to understand, since we don't have to partially copy an `OutlivesConstraint` into the `BlameConstraint` struct (which we can remove entirely) and the `BorrowExplanation::MustBeValidFor` enum variant (which we can simplify). Instead, we reference the "best" `OutlivesConstraint` by an index into `Vec`. This is not only simpler, it also opens up the possibility for diagnostics to access the full `OutlivesConstraint` info. --- .../src/diagnostics/conflict_errors.rs | 58 +++++++++-------- .../src/diagnostics/explain_borrow.rs | 25 +++----- .../src/diagnostics/region_errors.rs | 21 +++---- .../rustc_borrowck/src/region_infer/mod.rs | 63 ++++++++++--------- 4 files changed, 84 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 86e5a6700b039..ed6e06f8f7fe0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -46,6 +46,7 @@ use tracing::{debug, instrument}; use super::explain_borrow::{BorrowExplanation, LaterUseKind}; use super::{DescribePlaceOpt, RegionName, RegionNameSource, UseSpans}; use crate::borrow_set::{BorrowData, TwoPhaseActivation}; +use crate::consumers::OutlivesConstraint; use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead; use crate::diagnostics::{CapturedMessageOpt, call_kind, find_all_local_uses}; use crate::{InitializationRequiringAction, MirBorrowckCtxt, WriteKind, borrowck_errors}; @@ -3076,18 +3077,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ), ( Some(name), - BorrowExplanation::MustBeValidFor { - category: - category @ (ConstraintCategory::Return(_) - | ConstraintCategory::CallArgument(_) - | ConstraintCategory::OpaqueType), - from_closure: false, - ref region_name, - span, - .. - }, - ) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self - .report_escaping_closure_capture( + BorrowExplanation::MustBeValidFor { ref region_name, ref best_blame, .. }, + ) if let OutlivesConstraint { + category: + category @ (ConstraintCategory::Return(_) + | ConstraintCategory::CallArgument(_) + | ConstraintCategory::OpaqueType), + from_closure: false, + span, + .. + } = *best_blame.constraint() + && (borrow_spans.for_coroutine() || borrow_spans.for_closure()) => + { + self.report_escaping_closure_capture( borrow_spans, borrow_span, region_name, @@ -3095,21 +3097,28 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { span, &format!("`{name}`"), "function", - ), + ) + } ( name, BorrowExplanation::MustBeValidFor { - category: ConstraintCategory::Assignment, - from_closure: false, region_name: RegionName { source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), .. }, - span, + ref best_blame, .. }, - ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span), + ) if let OutlivesConstraint { + category: ConstraintCategory::Assignment, + from_closure: false, + span, + .. + } = *best_blame.constraint() => + { + self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span) + } (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( location, &name, @@ -3143,13 +3152,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { explanation: BorrowExplanation<'tcx>, ) -> Diag<'infcx> { let borrow_span = borrow_spans.var_or_use_path_span(); - if let BorrowExplanation::MustBeValidFor { - category, - span, - ref opt_place_desc, - from_closure: false, - .. - } = explanation + if let BorrowExplanation::MustBeValidFor { opt_place_desc, best_blame, .. } = &explanation + && let OutlivesConstraint { category, span, from_closure: false, .. } = + *best_blame.constraint() && let Err(diag) = self.try_report_cannot_return_reference_to_local( borrow, borrow_span, @@ -3356,8 +3361,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { proper_span: Span, explanation: BorrowExplanation<'tcx>, ) -> Diag<'infcx> { - if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } = - explanation + if let BorrowExplanation::MustBeValidFor { ref best_blame, .. } = explanation + && let OutlivesConstraint { category, span, from_closure: false, .. } = + *best_blame.constraint() { if let Err(diag) = self.try_report_cannot_return_reference_to_local( borrow, diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 15e3cf28aac32..a6a21ff2034c4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -20,9 +20,9 @@ use tracing::{debug, instrument}; use super::{RegionName, UseSpans, find_use}; use crate::borrow_set::BorrowData; -use crate::constraints::OutlivesConstraint; +use crate::consumers::OutlivesConstraint; use crate::nll::ConstraintDescription; -use crate::region_infer::{BlameConstraint, Cause}; +use crate::region_infer::{BestBlame, Cause}; use crate::{MirBorrowckCtxt, WriteKind}; #[derive(Debug)] @@ -35,12 +35,9 @@ pub(crate) enum BorrowExplanation<'tcx> { should_note_order: bool, }, MustBeValidFor { - category: ConstraintCategory<'tcx>, - from_closure: bool, - span: Span, + best_blame: BestBlame<'tcx>, region_name: RegionName, opt_place_desc: Option, - path: Vec>, }, Unexplained, } @@ -376,13 +373,13 @@ impl<'tcx> BorrowExplanation<'tcx> { } } BorrowExplanation::MustBeValidFor { - category, - span, ref region_name, ref opt_place_desc, - from_closure: _, - ref path, + ref best_blame, } => { + let OutlivesConstraint { category, span, .. } = *best_blame.constraint(); + let path = best_blame.path(); + region_name.highlight_region_name(err); if let Some(desc) = opt_place_desc { @@ -689,22 +686,18 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // Here, under NLL: no cause was found. Under polonius: no cause was found, or a // boring local was found, which we ignore like NLLs do to match its diagnostics. if let Some(region) = self.regioncx.to_error_region_vid(borrow_region_vid) { - let (blame_constraint, path) = self.regioncx.best_blame_constraint( + let best_blame = self.regioncx.best_blame_constraint( borrow_region_vid, NllRegionVariableOrigin::FreeRegion, region, ); - let BlameConstraint { category, from_closure, span, .. } = blame_constraint; if let Some(region_name) = self.give_region_a_name(region) { let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref()); BorrowExplanation::MustBeValidFor { - category, - from_closure, - span, region_name, opt_place_desc, - path, + best_blame, } } 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 a82cd5c74c330..112f3e2406943 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -28,9 +28,9 @@ use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource}; -use crate::consumers::RegionInferenceContext; +use crate::consumers::{OutlivesConstraint, RegionInferenceContext}; use crate::nll::ConstraintDescription; -use crate::region_infer::{BlameConstraint, TypeTest}; +use crate::region_infer::TypeTest; use crate::session_diagnostics::{ FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, @@ -414,9 +414,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }; // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. - 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); + let best_blame = self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid); + let cause = best_blame.to_obligation_cause(); // 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 @@ -447,9 +446,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) { debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); - let (blame_constraint, path) = - self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr); - let BlameConstraint { category, span, variance_info, .. } = blame_constraint; + let best_blame = self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr); + let OutlivesConstraint { category, span, variance_info, .. } = *best_blame.constraint(); + let path = best_blame.path(); debug!("report_region_error: category={:?} {:?} {:?}", category, span, variance_info); @@ -563,10 +562,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - self.add_placeholder_from_predicate_note(&mut diag, &path); - self.add_sized_or_copy_bound_info(&mut diag, category, &path); + self.add_placeholder_from_predicate_note(&mut diag, path); + self.add_sized_or_copy_bound_info(&mut diag, category, path); - for constraint in &path { + for constraint in path { if let ConstraintCategory::Cast { is_raw_ptr_dyn_type_cast: true, .. } = constraint.category { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 505aa74b04437..8895bdd1ef9ed 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1285,9 +1285,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { return RegionRelationCheckResult::Error; } - let blame_constraint = self - .best_blame_constraint(longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr) - .0; + let best_blame = self.best_blame_constraint( + longer_fr, + NllRegionVariableOrigin::FreeRegion, + shorter_fr, + ); + let OutlivesConstraint { category, span, .. } = *best_blame.constraint(); // Grow `shorter_fr` until we find some non-local regions. // We will always find at least one: `'static`. We'll call @@ -1346,8 +1349,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject: ClosureOutlivesSubject::Region(fr_minus), outlived_free_region: fr_plus, - blame_span: blame_constraint.span, - category: blame_constraint.category, + blame_span: span, + category, }); } return RegionRelationCheckResult::Propagated; @@ -1614,7 +1617,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { from_region: RegionVid, from_region_origin: NllRegionVariableOrigin<'tcx>, to_region: RegionVid, - ) -> (BlameConstraint<'tcx>, Vec>) { + ) -> BestBlame<'tcx> { assert!(from_region != to_region, "Trying to blame a region for itself!"); let path = self.constraint_path_between_regions(from_region, to_region).unwrap(); @@ -1787,13 +1790,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!(?best_choice, ?blame_source); - let best_constraint = if let Some(next) = path.get(best_choice + 1) + let best_blame_idx = if let Some(next) = path.get(best_choice + 1) && matches!(path[best_choice].category, ConstraintCategory::Return(_)) && next.category == ConstraintCategory::OpaqueType { // The return expression is being influenced by the return type being // impl Trait, point at the return type and not the return expr. - *next + best_choice + 1 } else if path[best_choice].category == ConstraintCategory::Return(ReturnConstraint::Normal) && let Some(field) = path.iter().find_map(|p| { if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None } @@ -1801,26 +1804,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { { path[best_choice].category = ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); - path[best_choice] + best_choice } else { - path[best_choice] + best_choice }; assert!( !matches!( - best_constraint.category, + path[best_blame_idx].category, ConstraintCategory::OutlivesUnnameablePlaceholder(_) ), "Illegal placeholder constraint blamed; should have redirected to other region relation" ); - let blame_constraint = BlameConstraint { - category: best_constraint.category, - from_closure: best_constraint.from_closure, - span: best_constraint.span, - variance_info: best_constraint.variance_info, - }; - (blame_constraint, path) + BestBlame { path, idx: best_blame_idx } } pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { @@ -1895,21 +1892,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { } #[derive(Clone, Debug)] -pub(crate) struct BlameConstraint<'tcx> { - pub category: ConstraintCategory<'tcx>, - pub from_closure: bool, - pub span: Span, - pub variance_info: ty::VarianceDiagInfo>, +pub(crate) struct BestBlame<'tcx> { + /// See docs on [`RegionInferenceContext::best_blame_constraint`] for what this is. + path: Vec>, + /// Index into `path` of the constraint most relevant to report to users. + idx: usize, } -impl<'tcx> BlameConstraint<'tcx> { - pub(crate) fn to_obligation_cause_from_path( - &self, - path: &[OutlivesConstraint<'tcx>], - ) -> ObligationCause<'tcx> { +impl<'tcx> BestBlame<'tcx> { + pub(crate) fn constraint(&self) -> &OutlivesConstraint<'tcx> { + &self.path[self.idx] + } + + pub(crate) fn path(&self) -> &[OutlivesConstraint<'tcx>] { + &self.path + } + + pub(crate) fn to_obligation_cause(&self) -> 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 + let cause_code = self + .path .iter() .find_map(|constraint| { if let ConstraintCategory::Predicate(predicate_span) = constraint.category { @@ -1923,6 +1926,6 @@ impl<'tcx> BlameConstraint<'tcx> { }) .unwrap_or_else(|| ObligationCauseCode::Misc); - ObligationCause::new(self.span, CRATE_DEF_ID, cause_code.clone()) + ObligationCause::new(self.constraint().span, CRATE_DEF_ID, cause_code.clone()) } }