Skip to content
Open
27 changes: 15 additions & 12 deletions compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_
use tracing::{debug, instrument};

use crate::MirBorrowckCtxt;
use crate::region_infer::values::RegionElement;
use crate::session_diagnostics::{
HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError,
};
Expand All @@ -49,11 +48,12 @@ impl<'tcx> UniverseInfo<'tcx> {
UniverseInfo::RelateTys { expected, found }
}

/// Report an error where an element erroneously made its way into `placeholder`.
pub(crate) fn report_erroneous_element(
&self,
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
placeholder: ty::PlaceholderRegion,
error_element: RegionElement,
error_element: Option<ty::PlaceholderRegion>,
cause: ObligationCause<'tcx>,
) {
match *self {
Expand Down Expand Up @@ -153,10 +153,17 @@ pub(crate) trait TypeOpInfo<'tcx> {
&self,
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
placeholder: ty::PlaceholderRegion,
error_element: RegionElement,
error_element: Option<ty::PlaceholderRegion>,
cause: ObligationCause<'tcx>,
) {
let tcx = mbcx.infcx.tcx;

// FIXME: this logic is convoluted and strange, and
// we should probably just use the placeholders we get
// as arguments! However, upstream error reporting code
// needs adaptations for that to work (this universe is
// neither the assigned one, nor the calculated one but
// some base-shifted one for some reason?).
let base_universe = self.base_universe();
debug!(?base_universe);

Expand All @@ -172,20 +179,16 @@ pub(crate) trait TypeOpInfo<'tcx> {
ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound },
);

let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) =
error_element
{
let adjusted_universe =
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
Copy link
Contributor Author

@amandasystems amandasystems Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the "checked sub" part is what really broke me here. Why are universes being adjusted? What's a base universe? And why can the subtraction sometimes overflow?!?!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record, these are the only reads of base_universe on CanonicalTypeOpDeeplyNormalizeGoal, CanonicalTypeOpAscribeUserTypeGoal and InstantiateOpaqueType.

// FIXME: one day this should just be error_element, but see above about the adjusted universes. At that point, this method can probably be removed entirely.
let error_region = error_element.and_then(|e| {
let adjusted_universe = e.universe.as_u32().checked_sub(base_universe.as_u32());
adjusted_universe.map(|adjusted| {
ty::Region::new_placeholder(
tcx,
ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound },
ty::Placeholder { universe: adjusted.into(), bound: e.bound },
)
})
} else {
None
};
});

debug!(?placeholder_region);

Expand Down
144 changes: 84 additions & 60 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,31 @@ pub(crate) enum RegionErrorKind<'tcx> {
/// A generic bound failure for a type test (`T: 'a`).
TypeTestError { type_test: TypeTest<'tcx> },

/// Higher-ranked subtyping error.
BoundUniversalRegionError {
/// 'a outlives 'b, and both are placeholders.
PlaceholderOutlivesPlaceholder {
rvid_a: RegionVid,
rvid_b: RegionVid,
origin_a: ty::PlaceholderRegion,
origin_b: ty::PlaceholderRegion,
},

/// Indicates that a placeholder has a universe too large for one
/// of its member existentials, or, equivalently, that there is
/// a path through the outlives constraint graph from a placeholder
/// to an existential region that cannot name it.
PlaceholderOutlivesExistentialThatCannotNameIt {
/// the placeholder that transitively outlives an
/// existential that shouldn't leak into it
longer_fr: RegionVid,
/// The existential leaking into `longer_fr`.
existential_that_cannot_name_longer: RegionVid,
// `longer_fr`'s originating placeholder region.
placeholder: ty::PlaceholderRegion,
},

/// Higher-ranked subtyping error. A placeholder outlives
/// either a location or a universal region.
PlaceholderOutlivesLocationOrUniversal {
/// The placeholder free region.
longer_fr: RegionVid,
/// The region element that erroneously must be outlived by `longer_fr`.
Expand Down Expand Up @@ -201,64 +224,38 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
&self,
diag: &mut Diag<'_>,
lower_bound: RegionVid,
) {
) -> Option<()> {
let tcx = self.infcx.tcx;

// find generic associated types in the given region 'lower_bound'
let gat_id_and_generics = self
.regioncx
.placeholders_contained_in(lower_bound)
.map(|placeholder| {
if let Some(id) = placeholder.bound.kind.get_id()
&& let Some(placeholder_id) = id.as_local()
&& let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
&& let Some(generics_impl) =
tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
{
Some((gat_hir_id, generics_impl))
} else {
None
}
})
.collect::<Vec<_>>();
debug!(?gat_id_and_generics);
let scc = self.regioncx.constraint_sccs().scc(lower_bound);
let placeholder: ty::PlaceholderRegion = self.regioncx.placeholder_representative(scc)?;
let placeholder_id = placeholder.bound.kind.get_id()?.as_local()?;
let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id);
let generics_impl =
self.infcx.tcx.parent_hir_node(self.infcx.tcx.parent_hir_id(gat_hir_id)).generics()?;

// Look for the where-bound which introduces the placeholder.
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
// and `T: for<'a> Trait`<'a>.
let mut hrtb_bounds = vec![];
gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| {
for pred in generics.predicates {
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
pred.kind
else {
continue;
};
if bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
for bound in *bounds {
hrtb_bounds.push(bound);
}
} else {
for bound in *bounds {
if let Trait(trait_bound) = bound {
if trait_bound
.bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
hrtb_bounds.push(bound);
return;
}
}
}

for pred in generics_impl.predicates {
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
pred.kind
else {
continue;
};
if bound_generic_params
.iter()
.rfind(|bgp| self.infcx.tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
for bound in *bounds {
hrtb_bounds.push(bound);
}
}
});
}
debug!(?hrtb_bounds);

let mut suggestions = vec![];
Expand Down Expand Up @@ -304,6 +301,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
Applicability::MaybeIncorrect,
);
}
Some(())
}

/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
Expand Down Expand Up @@ -361,30 +359,56 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}

RegionErrorKind::BoundUniversalRegionError {
RegionErrorKind::PlaceholderOutlivesLocationOrUniversal {
longer_fr,
placeholder,
error_element,
} => self.report_erroneous_rvid_reaches_placeholder(
longer_fr,
placeholder,
self.regioncx.region_from_element(longer_fr, &error_element),
),
RegionErrorKind::PlaceholderOutlivesPlaceholder {
rvid_a,
rvid_b,
origin_a,
origin_b,
} => {
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
debug!(
"Placeholder mismatch: {rvid_a:?} ({origin_a:?}) reaches {rvid_b:?} ({origin_b:?})"
);

// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
let cause = self
.regioncx
.best_blame_constraint(
longer_fr,
NllRegionVariableOrigin::Placeholder(placeholder),
error_vid,
rvid_a,
NllRegionVariableOrigin::Placeholder(origin_a),
rvid_b,
)
.0
.cause;

let universe = placeholder.universe;
let universe_info = self.regioncx.universe_info(universe);

universe_info.report_erroneous_element(self, placeholder, error_element, cause);
// FIXME We may be able to shorten the code path here, and immediately
// report a `RegionResolutionError::UpperBoundUniverseConflict`, but
// that's left for a future refactoring.
self.regioncx.universe_info(origin_a.universe).report_erroneous_element(
self,
origin_a,
Some(origin_b),
cause,
);
}

RegionErrorKind::PlaceholderOutlivesExistentialThatCannotNameIt {
longer_fr,
existential_that_cannot_name_longer,
placeholder,
} => self.report_erroneous_rvid_reaches_placeholder(
longer_fr,
placeholder,
existential_that_cannot_name_longer,
),

RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
if is_reported {
self.report_region_error(
Expand Down
Loading