Skip to content

Commit cf49ec3

Browse files
authored
Merge pull request #1522 from Nadrieril/hack-closure-lifetimes
Don't erase inner binders in trait resolution
2 parents a83a250 + 9fb15ed commit cf49ec3

File tree

2 files changed

+39
-16
lines changed

2 files changed

+39
-16
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: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,17 @@ 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 free regions from the given value. Largely copied from `tcx.erase_regions`, but also
171+
/// erases bound regions that are bound outside `value`, so we can call this function inside a
172+
/// `Binder`.
173+
fn erase_free_regions<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> T
172174
where
173175
T: TypeFoldable<TyCtxt<'tcx>>,
174176
{
175177
use rustc_middle::ty;
176178
struct RegionEraserVisitor<'tcx> {
177179
tcx: TyCtxt<'tcx>,
180+
depth: u32,
178181
}
179182

180183
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RegionEraserVisitor<'tcx> {
@@ -190,33 +193,53 @@ where
190193
where
191194
T: TypeFoldable<TyCtxt<'tcx>>,
192195
{
193-
// Empty the binder
194-
Binder::dummy(t.skip_binder().fold_with(self))
196+
let t = self.tcx.anonymize_bound_vars(t);
197+
self.depth += 1;
198+
let t = t.super_fold_with(self);
199+
self.depth -= 1;
200+
t
195201
}
196202

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
203+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
204+
// We don't erase bound regions that are bound inside the expression we started with,
205+
// but we do erase those that point "outside of it".
206+
match r.kind() {
207+
ty::ReBound(dbid, _) if dbid.as_u32() < self.depth => r,
208+
_ => self.tcx.lifetimes.re_erased,
209+
}
203210
}
204211
}
205-
value.fold_with(&mut RegionEraserVisitor { tcx })
212+
value.fold_with(&mut RegionEraserVisitor { tcx, depth: 0 })
206213
}
207214

208-
// Lifetimes are irrelevant when resolving instances.
215+
// Normalize and erase lifetimes, erasing more lifetimes than normal because we might be already
216+
// inside a binder and rustc doesn't like that.
209217
pub fn erase_and_norm<'tcx, T>(tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, x: T) -> T
210218
where
211219
T: TypeFoldable<TyCtxt<'tcx>> + Copy,
212220
{
213-
erase_all_regions(
221+
erase_free_regions(
214222
tcx,
215223
tcx.try_normalize_erasing_regions(typing_env, x)
216224
.unwrap_or(x),
217225
)
218226
}
219227

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

0 commit comments

Comments
 (0)