Skip to content

Allow escaping bound vars during normalize_erasing_regions in new solver #113471

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

Merged
merged 2 commits into from
Jul 14, 2023
Merged
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
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub use eval_ctxt::{
EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
};
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)]
enum SolverMode {
Expand Down
16 changes: 15 additions & 1 deletion compiler/rustc_trait_selection/src/solve/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,23 @@ use super::FulfillmentCtxt;
pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
at: At<'_, 'tcx>,
value: T,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
assert!(!value.has_escaping_bound_vars());
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.
///
/// Additionally takes a list of universes which represents the binders which have been
/// entered before passing `value` to the function.
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>>> {
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)
}
Expand Down
41 changes: 21 additions & 20 deletions compiler/rustc_trait_selection/src/traits/query/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,27 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
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() {
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![] }),
Err(_errors) => {
return Err(NoSolution);
Expand All @@ -81,27 +100,9 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
obligations: vec![],
cache: SsoHashMap::new(),
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);
info!(
"normalize::<{}>: result={:?} with {} obligations",
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}