Skip to content
73 changes: 37 additions & 36 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 @@ -145,52 +145,53 @@ pub(crate) trait TypeOpInfo<'tcx> {
error_region: Option<ty::Region<'tcx>>,
) -> Option<Diag<'infcx>>;

/// Constraints require that `error_element` appear in the
/// values of `placeholder`, but this cannot be proven to
/// hold. Report an error.
/// Turn a placeholder region into a Region with its universe adjusted by
/// the base universe.
fn region_with_adjusted_universe(
&self,
placeholder: ty::PlaceholderRegion,
tcx: TyCtxt<'tcx>,
) -> ty::Region<'tcx> {
let Some(adjusted_universe) =
placeholder.universe.as_u32().checked_sub(self.base_universe().as_u32())
else {
unreachable!(
"Could not adjust universe {:?} of {placeholder:?} by base universe {:?}",
placeholder.universe,
self.base_universe()
);
};
ty::Region::new_placeholder(
tcx,
ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound },
)
}

/// Report an error where an erroneous element reaches `placeholder`.
/// The erroneous element is either another placeholder that we provide,
/// or we reverse engineer what happened later anyway.
#[instrument(level = "debug", skip(self, mbcx))]
fn report_erroneous_element(
&self,
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
placeholder: ty::PlaceholderRegion,
error_element: RegionElement,
error_element: Option<ty::PlaceholderRegion>,
cause: ObligationCause<'tcx>,
) {
let tcx = mbcx.infcx.tcx;
let base_universe = self.base_universe();
debug!(?base_universe);

let Some(adjusted_universe) =
placeholder.universe.as_u32().checked_sub(base_universe.as_u32())
else {
mbcx.buffer_error(self.fallback_error(tcx, cause.span));
return;
};

let placeholder_region = ty::Region::new_placeholder(
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());
adjusted_universe.map(|adjusted| {
ty::Region::new_placeholder(
tcx,
ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound },
)
})
} else {
None
};
// FIXME: these adjusted universes are not (always) the same ones as we compute
// earlier. They probably should be, but the logic downstream is complicated,
// and assumes they use whatever this is.
//
// In fact, this function throws away a lot of interesting information that would
// probably allow bypassing lots of logic downstream for a much simpler flow.
let placeholder_region = self.region_with_adjusted_universe(placeholder, tcx);
let error_element = error_element.map(|e| self.region_with_adjusted_universe(e, tcx));

debug!(?placeholder_region);

let span = cause.span;
let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region);
let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_element);

debug!(?nice_error);
mbcx.buffer_error(nice_error.unwrap_or_else(|| self.fallback_error(tcx, span)));
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
let (blame_constraint, path) = self.regioncx.best_blame_constraint(
borrow_region,
NllRegionVariableOrigin::FreeRegion,
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
outlived_region,
);
let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;

Expand Down
7 changes: 3 additions & 4 deletions compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);

// Find a path between the borrow region and our opaque capture.
if let Some((path, _)) =
self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
r == opaque_region_vid
})
if let Some(path) = self
.regioncx
.constraint_path_between_regions(self.borrow_region, opaque_region_vid)
{
for constraint in path {
// If we find a call in this path, then check if it defines the opaque.
Expand Down
151 changes: 97 additions & 54 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
| ConstraintCategory::Boring
| ConstraintCategory::BoringNoLocation
| ConstraintCategory::Internal
| ConstraintCategory::IllegalUniverse => "",
| ConstraintCategory::IllegalPlaceholder(..) => "",
}
}
}
Expand Down Expand Up @@ -120,8 +120,31 @@ pub(crate) enum RegionErrorKind<'tcx> {
member_region: ty::Region<'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`.
existental_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 @@ -217,49 +240,39 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
&self,
diag: &mut Diag<'_>,
lower_bound: RegionVid,
) {
) -> Option<()> {
let mut suggestions = vec![];
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);

// find higher-ranked trait bounds bounded to the generic associated types
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()?;

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);
}

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);

hrtb_bounds.iter().for_each(|bound| {
Expand Down Expand Up @@ -304,6 +317,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 @@ -388,26 +402,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);

// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
let (_, cause) = self.regioncx.find_outlives_blame_span(
longer_fr,
NllRegionVariableOrigin::Placeholder(placeholder),
error_vid,
debug!(
"Placeholder mismatch: {rvid_a:?} ({origin_a:?}) reaches {rvid_b:?} ({origin_b:?})"
);

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

universe_info.report_erroneous_element(self, placeholder, error_element, cause);
let cause = self
.regioncx
.best_blame_constraint(
rvid_a,
NllRegionVariableOrigin::Placeholder(origin_a),
rvid_b,
)
.0
.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,
existental_that_cannot_name_longer,
placeholder,
} => self.report_erroneous_rvid_reaches_placeholder(
longer_fr,
placeholder,
existental_that_cannot_name_longer,
),

RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
if is_reported {
self.report_region_error(
Expand Down Expand Up @@ -456,9 +500,8 @@ 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, |r| {
self.regioncx.provides_universal_region(r, fr, outlived_fr)
});
let (blame_constraint, path) =
self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;

debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
Expand Down
Loading
Loading