Skip to content

Commit 94bed6e

Browse files
committed
Don't erase inner binders in trait resolution
1 parent 75184e1 commit 94bed6e

File tree

2 files changed

+36
-15
lines changed

2 files changed

+36
-15
lines changed

frontend/exporter/src/traits/resolution.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_trait_selection::traits::ImplSource;
1313

1414
use crate::{self_predicate, traits::utils::erase_and_norm};
1515

16-
use super::utils::{implied_predicates, required_predicates, ToPolyTraitRef};
16+
use super::utils::{implied_predicates, normalize_bound_val, required_predicates, ToPolyTraitRef};
1717

1818
#[derive(Debug, Clone)]
1919
pub enum PathChunk<'tcx> {
@@ -281,7 +281,7 @@ impl<'tcx> PredicateSearcher<'tcx> {
281281
let mut new_candidates = Vec::new();
282282
for mut candidate in candidates {
283283
// Normalize and erase all lifetimes.
284-
candidate.pred = erase_and_norm(tcx, self.typing_env, candidate.pred);
284+
candidate.pred = normalize_bound_val(tcx, self.typing_env, candidate.pred);
285285
if let Entry::Vacant(entry) = self.candidates.entry(candidate.pred) {
286286
entry.insert(candidate.clone());
287287
new_candidates.push(candidate);
@@ -421,7 +421,7 @@ impl<'tcx> PredicateSearcher<'tcx> {
421421
let tcx = self.tcx;
422422
let drop_trait = tcx.lang_items().drop_trait().unwrap();
423423

424-
let erased_tref = erase_and_norm(self.tcx, self.typing_env, *tref);
424+
let erased_tref = normalize_bound_val(self.tcx, self.typing_env, *tref);
425425
let trait_def_id = erased_tref.skip_binder().def_id;
426426

427427
let impl_source = shallow_resolve_trait_ref(tcx, self.typing_env.param_env, erased_tref);

frontend/exporter/src/traits/utils.rs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,15 @@ pub fn implied_predicates<'tcx>(
167167
}
168168
}
169169

170-
/// Erase all regions. Largely copied from `tcx.erase_regions`.
171-
pub fn erase_all_regions<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> T
170+
/// Erase all regions. Largely copied from `tcx.erase_regions`, but erases more regions.
171+
fn erase_all_regions<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> T
172172
where
173173
T: TypeFoldable<TyCtxt<'tcx>>,
174174
{
175175
use rustc_middle::ty;
176176
struct RegionEraserVisitor<'tcx> {
177177
tcx: TyCtxt<'tcx>,
178+
depth: u32,
178179
}
179180

180181
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RegionEraserVisitor<'tcx> {
@@ -190,22 +191,27 @@ where
190191
where
191192
T: TypeFoldable<TyCtxt<'tcx>>,
192193
{
193-
// Empty the binder
194-
Binder::dummy(t.skip_binder().fold_with(self))
194+
let t = self.tcx.anonymize_bound_vars(t);
195+
self.depth += 1;
196+
let t = t.super_fold_with(self);
197+
self.depth -= 1;
198+
t
195199
}
196200

197-
fn fold_region(&mut self, _r: ty::Region<'tcx>) -> ty::Region<'tcx> {
198-
// We erase bound regions despite it being possibly incorrect. `for<'a> fn(&'a
199-
// ())` and `fn(&'free ())` are different types: they may implement different
200-
// traits and have a different `TypeId`. It's unclear whether this can cause us
201-
// to select the wrong trait reference.
202-
self.tcx.lifetimes.re_erased
201+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
202+
// We don't erase bound regions that are bound inside the expression we started with,
203+
// but we do erase those that point "outside of it".
204+
match r.kind() {
205+
ty::ReBound(dbid, _) if dbid.as_u32() < self.depth => r,
206+
_ => self.tcx.lifetimes.re_erased,
207+
}
203208
}
204209
}
205-
value.fold_with(&mut RegionEraserVisitor { tcx })
210+
value.fold_with(&mut RegionEraserVisitor { tcx, depth: 0 })
206211
}
207212

208-
// Lifetimes are irrelevant when resolving instances.
213+
// Normalize and erase lifetimes, erasing more lifetimes than normal because we might be already
214+
// inside a binder and rustc doesn't like that.
209215
pub fn erase_and_norm<'tcx, T>(tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, x: T) -> T
210216
where
211217
T: TypeFoldable<TyCtxt<'tcx>> + Copy,
@@ -217,6 +223,21 @@ where
217223
)
218224
}
219225

226+
/// Given our currently hacky handling of binders, in order for trait resolution to work we must
227+
/// empty out the binders of trait refs. Specifically it's so that we can reconnect associated type
228+
/// constraints with the trait ref they come from, given that the projection in question doesn't
229+
/// track the right binder currently.
230+
pub fn normalize_bound_val<'tcx, T>(
231+
tcx: TyCtxt<'tcx>,
232+
typing_env: TypingEnv<'tcx>,
233+
x: Binder<'tcx, T>,
234+
) -> Binder<'tcx, T>
235+
where
236+
T: TypeFoldable<TyCtxt<'tcx>> + Copy,
237+
{
238+
Binder::dummy(erase_and_norm(tcx, typing_env, x.skip_binder()))
239+
}
240+
220241
pub trait ToPolyTraitRef<'tcx> {
221242
fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
222243
}

0 commit comments

Comments
 (0)