Skip to content

Commit ad96323

Browse files
committed
Auto merge of #113471 - compiler-errors:new-solver-norm-escaping, r=lcnr
Allow escaping bound vars during `normalize_erasing_regions` in new solver Add `AllowEscapingBoundVars` to `deeply_normalize`, and use it in the new solver in the `query_normalize` routine. Ideally, we'd make all `query_normalize` calls handle pass in `AllowEscapingBoundVars` individually, because really the only `query_normalize` call that needs `AllowEscapingBoundVars::Yes` is the one in `try_normalize_generic_arg_after_erasing_regions`, but I think that's kind of overkill. I am happy to be convinced otherwise, though. r? `@lcnr`
2 parents 3b55d23 + 1ef85d8 commit ad96323

File tree

4 files changed

+55
-22
lines changed

4 files changed

+55
-22
lines changed

compiler/rustc_trait_selection/src/solve/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub use eval_ctxt::{
3737
EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
3838
};
3939
pub use fulfill::FulfillmentCtxt;
40-
pub(crate) use normalize::deeply_normalize;
40+
pub(crate) use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
4141

4242
#[derive(Debug, Clone, Copy)]
4343
enum SolverMode {

compiler/rustc_trait_selection/src/solve/normalize.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,23 @@ use super::FulfillmentCtxt;
1919
pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
2020
at: At<'_, 'tcx>,
2121
value: T,
22+
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
23+
assert!(!value.has_escaping_bound_vars());
24+
deeply_normalize_with_skipped_universes(at, value, vec![])
25+
}
26+
27+
/// Deeply normalize all aliases in `value`. This does not handle inference and expects
28+
/// its input to be already fully resolved.
29+
///
30+
/// Additionally takes a list of universes which represents the binders which have been
31+
/// entered before passing `value` to the function.
32+
pub(crate) fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
33+
at: At<'_, 'tcx>,
34+
value: T,
35+
universes: Vec<Option<UniverseIndex>>,
2236
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
2337
let fulfill_cx = FulfillmentCtxt::new(at.infcx);
24-
let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes: Vec::new() };
38+
let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes };
2539

2640
value.try_fold_with(&mut folder)
2741
}

compiler/rustc_trait_selection/src/traits/query/normalize.rs

+21-20
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,27 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
6161
self.cause,
6262
);
6363

64+
// This is actually a consequence by the way `normalize_erasing_regions` works currently.
65+
// Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
66+
// through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
67+
// with trying to normalize with escaping bound vars.
68+
//
69+
// Here, we just add the universes that we *would* have created had we passed through the binders.
70+
//
71+
// We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
72+
// The rest of the code is already set up to be lazy about replacing bound vars,
73+
// and only when we actually have to normalize.
74+
let universes = if value.has_escaping_bound_vars() {
75+
let mut max_visitor =
76+
MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
77+
value.visit_with(&mut max_visitor);
78+
vec![None; max_visitor.escaping]
79+
} else {
80+
vec![]
81+
};
82+
6483
if self.infcx.next_trait_solver() {
65-
match crate::solve::deeply_normalize(self, value) {
84+
match crate::solve::deeply_normalize_with_skipped_universes(self, value, universes) {
6685
Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
6786
Err(_errors) => {
6887
return Err(NoSolution);
@@ -81,27 +100,9 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
81100
obligations: vec![],
82101
cache: SsoHashMap::new(),
83102
anon_depth: 0,
84-
universes: vec![],
103+
universes,
85104
};
86105

87-
// This is actually a consequence by the way `normalize_erasing_regions` works currently.
88-
// Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
89-
// through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
90-
// with trying to normalize with escaping bound vars.
91-
//
92-
// Here, we just add the universes that we *would* have created had we passed through the binders.
93-
//
94-
// We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
95-
// The rest of the code is already set up to be lazy about replacing bound vars,
96-
// and only when we actually have to normalize.
97-
if value.has_escaping_bound_vars() {
98-
let mut max_visitor =
99-
MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
100-
value.visit_with(&mut max_visitor);
101-
if max_visitor.escaping > 0 {
102-
normalizer.universes.extend((0..max_visitor.escaping).map(|_| None));
103-
}
104-
}
105106
let result = value.try_fold_with(&mut normalizer);
106107
info!(
107108
"normalize::<{}>: result={:?} with {} obligations",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
trait Trivial {
5+
type Assoc;
6+
}
7+
8+
impl<T: ?Sized> Trivial for T {
9+
type Assoc = ();
10+
}
11+
12+
fn main() {
13+
// During writeback, we call `normalize_erasing_regions`, which will walk past
14+
// the `for<'a>` binder and try to normalize `<&'a () as Trivial>::Assoc` directly.
15+
// We need to handle this case in the new deep normalizer similarly to how it
16+
// is handled in the old solver.
17+
let x: Option<for<'a> fn(<&'a () as Trivial>::Assoc)> = None;
18+
}

0 commit comments

Comments
 (0)