Allow escaping bound vars during normalize_erasing_regions in new solver

This commit is contained in:
Michael Goulet 2023-07-13 18:24:37 +00:00
parent bacf5bcbc7
commit 4bcca3294a
4 changed files with 51 additions and 22 deletions

View File

@ -37,7 +37,7 @@ pub use eval_ctxt::{
EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache, EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
}; };
pub use fulfill::FulfillmentCtxt; pub use fulfill::FulfillmentCtxt;
pub(crate) use normalize::deeply_normalize; pub(crate) use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum SolverMode { enum SolverMode {

View File

@ -19,9 +19,19 @@ use super::FulfillmentCtxt;
pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
at: At<'_, 'tcx>, at: At<'_, 'tcx>,
value: T, value: T,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
deeply_normalize_with_skipped_universes(at, value, vec![])
}
/// Deeply normalize all aliases in `value`. This does not handle inference and expects
/// its input to be already fully resolved.
pub(crate) fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
at: At<'_, 'tcx>,
value: T,
universes: Vec<Option<UniverseIndex>>,
) -> Result<T, Vec<FulfillmentError<'tcx>>> { ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
let fulfill_cx = FulfillmentCtxt::new(at.infcx); let fulfill_cx = FulfillmentCtxt::new(at.infcx);
let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes: Vec::new() }; let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes };
value.try_fold_with(&mut folder) value.try_fold_with(&mut folder)
} }

View File

@ -61,8 +61,27 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
self.cause, self.cause,
); );
// This is actually a consequence by the way `normalize_erasing_regions` works currently.
// Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
// through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
// with trying to normalize with escaping bound vars.
//
// Here, we just add the universes that we *would* have created had we passed through the binders.
//
// We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
// The rest of the code is already set up to be lazy about replacing bound vars,
// and only when we actually have to normalize.
let universes = if value.has_escaping_bound_vars() {
let mut max_visitor =
MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
value.visit_with(&mut max_visitor);
vec![None; max_visitor.escaping]
} else {
vec![]
};
if self.infcx.next_trait_solver() { if self.infcx.next_trait_solver() {
match crate::solve::deeply_normalize(self, value) { match crate::solve::deeply_normalize_with_skipped_universes(self, value, universes) {
Ok(value) => return Ok(Normalized { value, obligations: vec![] }), Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
Err(_errors) => { Err(_errors) => {
return Err(NoSolution); return Err(NoSolution);
@ -81,27 +100,9 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
obligations: vec![], obligations: vec![],
cache: SsoHashMap::new(), cache: SsoHashMap::new(),
anon_depth: 0, anon_depth: 0,
universes: vec![], universes,
}; };
// This is actually a consequence by the way `normalize_erasing_regions` works currently.
// Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
// through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
// with trying to normalize with escaping bound vars.
//
// Here, we just add the universes that we *would* have created had we passed through the binders.
//
// We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
// The rest of the code is already set up to be lazy about replacing bound vars,
// and only when we actually have to normalize.
if value.has_escaping_bound_vars() {
let mut max_visitor =
MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
value.visit_with(&mut max_visitor);
if max_visitor.escaping > 0 {
normalizer.universes.extend((0..max_visitor.escaping).map(|_| None));
}
}
let result = value.try_fold_with(&mut normalizer); let result = value.try_fold_with(&mut normalizer);
info!( info!(
"normalize::<{}>: result={:?} with {} obligations", "normalize::<{}>: result={:?} with {} obligations",

View File

@ -0,0 +1,18 @@
// compile-flags: -Ztrait-solver=next
// check-pass
trait Trivial {
type Assoc;
}
impl<T: ?Sized> Trivial for T {
type Assoc = ();
}
fn main() {
// During writeback, we call `normalize_erasing_regions`, which will walk past
// the `for<'a>` binder and try to normalize `<&'a () as Trivial>::Assoc` directly.
// We need to handle this case in the new deep normalizer similarly to how it
// is handled in the old solver.
let x: Option<for<'a> fn(<&'a () as Trivial>::Assoc)> = None;
}