Skip to content

[WIP] Move placeholder error handling to before region inference #142623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 37 additions & 35 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,54 @@ 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
88 changes: 70 additions & 18 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 @@ -388,26 +411,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 +509,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