Auto merge of #119101 - compiler-errors:outlives, r=lcnr
Normalize region obligation in lexical region resolution with next-gen solver This normalizes region obligations when we `resolve_regions`, since they may be unnormalized with deferred projection equality. It's pretty hard to add tests that exercise this without also triggering MIR borrowck errors (because we don't normalize there yet). I've added one test with two revisions that should test that we both 1. normalize region obligations in the param env, and 2. normalize registered region obligations during lexical region resolution.
This commit is contained in:
commit
cb4d9a1902
25 changed files with 211 additions and 70 deletions
|
@ -518,6 +518,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
|
||||
self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit();
|
||||
}
|
||||
|
||||
RegionResolutionError::CannotNormalize(ty, origin) => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.struct_span_err(origin.span(), format!("cannot normalize `{ty}`"))
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -559,7 +566,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
RegionResolutionError::GenericBoundFailure(..) => true,
|
||||
RegionResolutionError::ConcreteFailure(..)
|
||||
| RegionResolutionError::SubSupConflict(..)
|
||||
| RegionResolutionError::UpperBoundUniverseConflict(..) => false,
|
||||
| RegionResolutionError::UpperBoundUniverseConflict(..)
|
||||
| RegionResolutionError::CannotNormalize(..) => false,
|
||||
};
|
||||
|
||||
let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
|
||||
|
@ -574,6 +582,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
|
||||
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
|
||||
RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
|
||||
RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
|
||||
});
|
||||
errors
|
||||
}
|
||||
|
|
|
@ -98,6 +98,8 @@ pub enum RegionResolutionError<'tcx> {
|
|||
SubregionOrigin<'tcx>, // cause of the constraint
|
||||
Region<'tcx>, // the placeholder `'b`
|
||||
),
|
||||
|
||||
CannotNormalize(Ty<'tcx>, SubregionOrigin<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> RegionResolutionError<'tcx> {
|
||||
|
@ -106,7 +108,8 @@ impl<'tcx> RegionResolutionError<'tcx> {
|
|||
RegionResolutionError::ConcreteFailure(origin, _, _)
|
||||
| RegionResolutionError::GenericBoundFailure(origin, _, _)
|
||||
| RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _)
|
||||
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) => origin,
|
||||
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _)
|
||||
| RegionResolutionError::CannotNormalize(_, origin) => origin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Various code related to computing outlives relations.
|
||||
use self::env::OutlivesEnvironment;
|
||||
use super::region_constraints::RegionConstraintData;
|
||||
use super::{InferCtxt, RegionResolutionError};
|
||||
use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
|
||||
use crate::infer::free_regions::RegionRelations;
|
||||
use crate::infer::lexical_region_resolve;
|
||||
use rustc_middle::traits::query::OutlivesBound;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
pub mod components;
|
||||
pub mod env;
|
||||
|
@ -41,12 +41,22 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
/// result. After this, no more unification operations should be
|
||||
/// done -- or the compiler will panic -- but it is legal to use
|
||||
/// `resolve_vars_if_possible` as well as `fully_resolve`.
|
||||
///
|
||||
/// If you are in a crate that has access to `rustc_trait_selection`,
|
||||
/// then it's probably better to use `resolve_regions`,
|
||||
/// which knows how to normalize registered region obligations.
|
||||
#[must_use]
|
||||
pub fn resolve_regions(
|
||||
pub fn resolve_regions_with_normalize(
|
||||
&self,
|
||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||
deeply_normalize_ty: impl Fn(Ty<'tcx>, SubregionOrigin<'tcx>) -> Result<Ty<'tcx>, Ty<'tcx>>,
|
||||
) -> Vec<RegionResolutionError<'tcx>> {
|
||||
self.process_registered_region_obligations(outlives_env);
|
||||
match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty) {
|
||||
Ok(()) => {}
|
||||
Err((ty, origin)) => {
|
||||
return vec![RegionResolutionError::CannotNormalize(ty, origin)];
|
||||
}
|
||||
};
|
||||
|
||||
let (var_infos, data) = {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
|
|
@ -70,6 +70,7 @@ use rustc_data_structures::undo_log::UndoLogs;
|
|||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use smallvec::smallvec;
|
||||
|
||||
use super::env::OutlivesEnvironment;
|
||||
|
@ -123,26 +124,54 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
/// flow of the inferencer. The key point is that it is
|
||||
/// invoked after all type-inference variables have been bound --
|
||||
/// right before lexical region resolution.
|
||||
#[instrument(level = "debug", skip(self, outlives_env))]
|
||||
pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
|
||||
#[instrument(level = "debug", skip(self, outlives_env, deeply_normalize_ty))]
|
||||
pub fn process_registered_region_obligations<E>(
|
||||
&self,
|
||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||
mut deeply_normalize_ty: impl FnMut(Ty<'tcx>, SubregionOrigin<'tcx>) -> Result<Ty<'tcx>, E>,
|
||||
) -> Result<(), (E, SubregionOrigin<'tcx>)> {
|
||||
assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");
|
||||
|
||||
let normalized_caller_bounds: Vec<_> = outlives_env
|
||||
.param_env
|
||||
.caller_bounds()
|
||||
.iter()
|
||||
.filter_map(|clause| {
|
||||
let bound_clause = clause.kind();
|
||||
let ty::ClauseKind::TypeOutlives(outlives) = bound_clause.skip_binder() else {
|
||||
return None;
|
||||
};
|
||||
Some(
|
||||
deeply_normalize_ty(
|
||||
outlives.0,
|
||||
SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP),
|
||||
)
|
||||
.map(|ty| bound_clause.rebind(ty::OutlivesPredicate(ty, outlives.1))),
|
||||
)
|
||||
})
|
||||
// FIXME(-Znext-solver): How do we accurately report an error here :(
|
||||
.try_collect()
|
||||
.map_err(|e| (e, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP)))?;
|
||||
|
||||
let my_region_obligations = self.take_registered_region_obligations();
|
||||
|
||||
for RegionObligation { sup_type, sub_region, origin } in my_region_obligations {
|
||||
let sup_type =
|
||||
deeply_normalize_ty(sup_type, origin.clone()).map_err(|e| (e, origin.clone()))?;
|
||||
debug!(?sup_type, ?sub_region, ?origin);
|
||||
let sup_type = self.resolve_vars_if_possible(sup_type);
|
||||
|
||||
let outlives = &mut TypeOutlives::new(
|
||||
self,
|
||||
self.tcx,
|
||||
outlives_env.region_bound_pairs(),
|
||||
None,
|
||||
outlives_env.param_env,
|
||||
&normalized_caller_bounds,
|
||||
);
|
||||
let category = origin.to_constraint_category();
|
||||
outlives.type_must_outlive(origin, sup_type, sub_region, category);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +219,7 @@ where
|
|||
tcx: TyCtxt<'tcx>,
|
||||
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
|
||||
) -> Self {
|
||||
Self {
|
||||
delegate,
|
||||
|
@ -199,7 +228,7 @@ where
|
|||
tcx,
|
||||
region_bound_pairs,
|
||||
implicit_region_bound,
|
||||
param_env,
|
||||
caller_bounds,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ pub struct VerifyBoundCx<'cx, 'tcx> {
|
|||
/// Outside of borrowck the only way to prove `T: '?0` is by
|
||||
/// setting `'?0` to `'empty`.
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||
|
@ -31,9 +31,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
|
||||
) -> Self {
|
||||
Self { tcx, region_bound_pairs, implicit_region_bound, param_env }
|
||||
Self { tcx, region_bound_pairs, implicit_region_bound, caller_bounds }
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
|
@ -219,8 +219,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
// To start, collect bounds from user environment. Note that
|
||||
// parameter environments are already elaborated, so we don't
|
||||
// have to worry about that.
|
||||
let c_b = self.param_env.caller_bounds();
|
||||
let param_bounds = self.collect_outlives_from_clause_list(erased_ty, c_b.into_iter());
|
||||
let param_bounds = self.caller_bounds.iter().copied().filter(move |outlives_predicate| {
|
||||
super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty)
|
||||
});
|
||||
|
||||
// Next, collect regions we scraped from the well-formedness
|
||||
// constraints in the fn signature. To do that, we walk the list
|
||||
|
@ -307,22 +308,4 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
.filter_map(|p| p.no_bound_vars())
|
||||
.map(|OutlivesPredicate(_, r)| r)
|
||||
}
|
||||
|
||||
/// Searches through a predicate list for a predicate `T: 'a`.
|
||||
///
|
||||
/// Careful: does not elaborate predicates, and just uses `==`
|
||||
/// when comparing `ty` for equality, so `ty` must be something
|
||||
/// that does not involve inference variables and where you
|
||||
/// otherwise want a precise match.
|
||||
fn collect_outlives_from_clause_list(
|
||||
&self,
|
||||
erased_ty: Ty<'tcx>,
|
||||
clauses: impl Iterator<Item = ty::Clause<'tcx>>,
|
||||
) -> impl Iterator<Item = ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>>
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
clauses.filter_map(|p| p.as_type_outlives_clause()).filter(move |outlives_predicate| {
|
||||
super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#![feature(extend_one)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iterator_try_collect)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "512"] // For rustdoc
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue