Skip to content

Commit 0afc208

Browse files
committed
Auto merge of #85499 - jackh726:assoc-type-norm-rebase, r=nikomatsakis
Normalize projections under binders Fixes #70243 Fixes #70120 Fixes #62529 Fixes #87219 Issues to followup on after (probably fixed, but no test added here): #76956 #56556 #79207 #85636 r? `@nikomatsakis`
2 parents 7b0e554 + 994a6bb commit 0afc208

File tree

55 files changed

+811
-391
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+811
-391
lines changed

compiler/rustc_middle/src/ty/layout.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -2483,10 +2483,9 @@ impl<'tcx> ty::Instance<'tcx> {
24832483
// `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping
24842484
// track of a polymorphization `ParamEnv` to allow normalizing later.
24852485
let mut sig = match *ty.kind() {
2486-
ty::FnDef(def_id, substs) if tcx.sess.opts.debugging_opts.polymorphize => tcx
2486+
ty::FnDef(def_id, substs) => tcx
24872487
.normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id))
24882488
.subst(tcx, substs),
2489-
ty::FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs),
24902489
_ => unreachable!(),
24912490
};
24922491

compiler/rustc_mir/src/borrow_check/type_check/input_output.rs

+44-10
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
1010
use rustc_infer::infer::LateBoundRegionConversionTime;
1111
use rustc_middle::mir::*;
12-
use rustc_middle::ty::Ty;
12+
use rustc_middle::traits::ObligationCause;
13+
use rustc_middle::ty::{self, Ty};
14+
use rustc_trait_selection::traits::query::normalize::AtExt;
1315

1416
use rustc_index::vec::Idx;
1517
use rustc_span::Span;
@@ -162,17 +164,49 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
162164
fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
163165
debug!("equate_normalized_input_or_output(a={:?}, b={:?})", a, b);
164166

165-
if let Err(terr) =
167+
if let Err(_) =
166168
self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
167169
{
168-
span_mirbug!(
169-
self,
170-
Location::START,
171-
"equate_normalized_input_or_output: `{:?}=={:?}` failed with `{:?}`",
172-
a,
173-
b,
174-
terr
175-
);
170+
// FIXME(jackh726): This is a hack. It's somewhat like
171+
// `rustc_traits::normalize_after_erasing_regions`. Ideally, we'd
172+
// like to normalize *before* inserting into `local_decls`, but
173+
// doing so ends up causing some other trouble.
174+
let b = match self
175+
.infcx
176+
.at(&ObligationCause::dummy(), ty::ParamEnv::empty())
177+
.normalize(b)
178+
{
179+
Ok(n) => {
180+
debug!("equate_inputs_and_outputs: {:?}", n);
181+
if n.obligations.iter().all(|o| {
182+
matches!(
183+
o.predicate.kind().skip_binder(),
184+
ty::PredicateKind::RegionOutlives(_)
185+
| ty::PredicateKind::TypeOutlives(_)
186+
)
187+
}) {
188+
n.value
189+
} else {
190+
b
191+
}
192+
}
193+
Err(_) => {
194+
debug!("equate_inputs_and_outputs: NoSolution");
195+
b
196+
}
197+
};
198+
if let Err(terr) =
199+
self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
200+
{
201+
span_mirbug!(
202+
self,
203+
Location::START,
204+
"equate_normalized_input_or_output: `{:?}=={:?}` failed with `{:?}`",
205+
a,
206+
b,
207+
terr
208+
);
209+
}
176210
}
177211
}
178212
}

compiler/rustc_mir/src/borrow_check/type_check/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
10531053
);
10541054
for user_annotation in self.user_type_annotations {
10551055
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
1056+
let inferred_ty = self.normalize(inferred_ty, Locations::All(span));
10561057
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
10571058
match annotation {
10581059
UserType::Ty(mut ty) => {

compiler/rustc_trait_selection/src/traits/project.rs

+59-44
Original file line numberDiff line numberDiff line change
@@ -362,25 +362,40 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
362362
if !needs_normalization(&ty, self.param_env.reveal()) {
363363
return ty;
364364
}
365-
// We don't want to normalize associated types that occur inside of region
366-
// binders, because they may contain bound regions, and we can't cope with that.
365+
366+
// We try to be a little clever here as a performance optimization in
367+
// cases where there are nested projections under binders.
368+
// For example:
369+
// ```
370+
// for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>)
371+
// ```
372+
// We normalize the substs on the projection before the projecting, but
373+
// if we're naive, we'll
374+
// replace bound vars on inner, project inner, replace placeholders on inner,
375+
// replace bound vars on outer, project outer, replace placeholders on outer
367376
//
368-
// Example:
377+
// However, if we're a bit more clever, we can replace the bound vars
378+
// on the entire type before normalizing nested projections, meaning we
379+
// replace bound vars on outer, project inner,
380+
// project outer, replace placeholders on outer
369381
//
370-
// for<'a> fn(<T as Foo<&'a>>::A)
382+
// This is possible because the inner `'a` will already be a placeholder
383+
// when we need to normalize the inner projection
371384
//
372-
// Instead of normalizing `<T as Foo<&'a>>::A` here, we'll
373-
// normalize it when we instantiate those bound regions (which
374-
// should occur eventually).
385+
// On the other hand, this does add a bit of complexity, since we only
386+
// replace bound vars if the current type is a `Projection` and we need
387+
// to make sure we don't forget to fold the substs regardless.
375388

376-
let ty = ty.super_fold_with(self);
377389
match *ty.kind() {
378-
ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => {
390+
ty::Opaque(def_id, substs) => {
379391
// Only normalize `impl Trait` after type-checking, usually in codegen.
380392
match self.param_env.reveal() {
381-
Reveal::UserFacing => ty,
393+
Reveal::UserFacing => ty.super_fold_with(self),
382394

383395
Reveal::All => {
396+
// N.b. there is an assumption here all this code can handle
397+
// escaping bound vars.
398+
384399
let recursion_limit = self.tcx().recursion_limit();
385400
if !recursion_limit.value_within_limit(self.depth) {
386401
let obligation = Obligation::with_depth(
@@ -392,6 +407,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
392407
self.selcx.infcx().report_overflow_error(&obligation, true);
393408
}
394409

410+
let substs = substs.super_fold_with(self);
395411
let generic_ty = self.tcx().type_of(def_id);
396412
let concrete_ty = generic_ty.subst(self.tcx(), substs);
397413
self.depth += 1;
@@ -403,18 +419,13 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
403419
}
404420

405421
ty::Projection(data) if !data.has_escaping_bound_vars() => {
406-
// This is kind of hacky -- we need to be able to
407-
// handle normalization within binders because
408-
// otherwise we wind up a need to normalize when doing
409-
// trait matching (since you can have a trait
410-
// obligation like `for<'a> T::B: Fn(&'a i32)`), but
411-
// we can't normalize with bound regions in scope. So
412-
// far now we just ignore binders but only normalize
413-
// if all bound regions are gone (and then we still
414-
// have to renormalize whenever we instantiate a
415-
// binder). It would be better to normalize in a
416-
// binding-aware fashion.
422+
// This branch is *mostly* just an optimization: when we don't
423+
// have escaping bound vars, we don't need to replace them with
424+
// placeholders (see branch below). *Also*, we know that we can
425+
// register an obligation to *later* project, since we know
426+
// there won't be bound vars there.
417427

428+
let data = data.super_fold_with(self);
418429
let normalized_ty = normalize_projection_type(
419430
self.selcx,
420431
self.param_env,
@@ -433,22 +444,23 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
433444
normalized_ty
434445
}
435446

436-
ty::Projection(data) if !data.trait_ref(self.tcx()).has_escaping_bound_vars() => {
437-
// Okay, so you thought the previous branch was hacky. Well, to
438-
// extend upon this, when the *trait ref* doesn't have escaping
439-
// bound vars, but the associated item *does* (can only occur
440-
// with GATs), then we might still be able to project the type.
441-
// For this, we temporarily replace the bound vars with
442-
// placeholders. Note though, that in the case that we still
443-
// can't project for whatever reason (e.g. self type isn't
444-
// known enough), we *can't* register an obligation and return
445-
// an inference variable (since then that obligation would have
446-
// bound vars and that's a can of worms). Instead, we just
447-
// give up and fall back to pretending like we never tried!
447+
ty::Projection(data) => {
448+
// If there are escaping bound vars, we temporarily replace the
449+
// bound vars with placeholders. Note though, that in the case
450+
// that we still can't project for whatever reason (e.g. self
451+
// type isn't known enough), we *can't* register an obligation
452+
// and return an inference variable (since then that obligation
453+
// would have bound vars and that's a can of worms). Instead,
454+
// we just give up and fall back to pretending like we never tried!
455+
//
456+
// Note: this isn't necessarily the final approach here; we may
457+
// want to figure out how to register obligations with escaping vars
458+
// or handle this some other way.
448459

449460
let infcx = self.selcx.infcx();
450461
let (data, mapped_regions, mapped_types, mapped_consts) =
451462
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
463+
let data = data.super_fold_with(self);
452464
let normalized_ty = opt_normalize_projection_type(
453465
self.selcx,
454466
self.param_env,
@@ -459,16 +471,18 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
459471
)
460472
.ok()
461473
.flatten()
462-
.unwrap_or_else(|| ty);
463-
464-
let normalized_ty = PlaceholderReplacer::replace_placeholders(
465-
infcx,
466-
mapped_regions,
467-
mapped_types,
468-
mapped_consts,
469-
&self.universes,
470-
normalized_ty,
471-
);
474+
.map(|normalized_ty| {
475+
PlaceholderReplacer::replace_placeholders(
476+
infcx,
477+
mapped_regions,
478+
mapped_types,
479+
mapped_consts,
480+
&self.universes,
481+
normalized_ty,
482+
)
483+
})
484+
.unwrap_or_else(|| ty.super_fold_with(self));
485+
472486
debug!(
473487
?self.depth,
474488
?ty,
@@ -479,7 +493,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
479493
normalized_ty
480494
}
481495

482-
_ => ty,
496+
_ => ty.super_fold_with(self),
483497
}
484498
}
485499

@@ -908,6 +922,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
908922
// an impl, where-clause etc) and hence we must
909923
// re-normalize it
910924

925+
let projected_ty = selcx.infcx().resolve_vars_if_possible(projected_ty);
911926
debug!(?projected_ty, ?depth, ?projected_obligations);
912927

913928
let result = if projected_ty.has_projections() {

0 commit comments

Comments
 (0)