Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit f52b2d8

Browse files
committed
Avoid cycle in nested obligations for object candidate
Bounds of the form `type Future: Future<Result=Self::Result>` exist in some ecosystem crates. To validate these bounds for trait objects we need to normalize `Self::Result` in a way that doesn't cause a cycle.
1 parent 582ccec commit f52b2d8

12 files changed

+382
-52
lines changed

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,7 @@ impl<'tcx> ExistentialProjection<'tcx> {
15131513
/// then this function would return a `exists T. T: Iterator` existential trait
15141514
/// reference.
15151515
pub fn trait_ref(&self, tcx: TyCtxt<'_>) -> ty::ExistentialTraitRef<'tcx> {
1516+
// FIXME(generic_associated_types): truncate substs to have the right length.
15161517
let def_id = tcx.associated_item(self.item_def_id).container.id();
15171518
ty::ExistentialTraitRef { def_id, substs: self.substs }
15181519
}

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

Lines changed: 175 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
use rustc_data_structures::stack::ensure_sufficient_stack;
1010
use rustc_hir::lang_items::LangItem;
1111
use rustc_index::bit_set::GrowableBitSet;
12+
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
1213
use rustc_infer::infer::{self, InferOk};
14+
use rustc_middle::ty::fold::TypeFolder;
1315
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
14-
use rustc_middle::ty::{self, Ty};
16+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
1517
use rustc_middle::ty::{ToPolyTraitRef, ToPredicate, WithConstness};
1618
use rustc_span::def_id::DefId;
1719

18-
use crate::traits::project::{self, normalize_with_depth};
20+
use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
1921
use crate::traits::select::TraitObligationExt;
2022
use crate::traits::util;
2123
use crate::traits::util::{closure_trait_ref_and_return_type, predicate_for_trait_def};
@@ -340,16 +342,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
340342
&mut self,
341343
obligation: &TraitObligation<'tcx>,
342344
) -> ImplSourceObjectData<'tcx, PredicateObligation<'tcx>> {
345+
let tcx = self.tcx();
343346
debug!("confirm_object_candidate({:?})", obligation);
344347

345-
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
346-
let self_ty = self.infcx.replace_bound_vars_with_placeholders(&self_ty);
348+
let trait_predicate =
349+
self.infcx.replace_bound_vars_with_placeholders(&obligation.predicate);
350+
let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty());
351+
let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref);
347352
let data = match self_ty.kind() {
348-
ty::Dynamic(data, ..) => data,
353+
ty::Dynamic(data, ..) => {
354+
self.infcx
355+
.replace_bound_vars_with_fresh_vars(
356+
obligation.cause.span,
357+
HigherRankedType,
358+
data,
359+
)
360+
.0
361+
}
349362
_ => span_bug!(obligation.cause.span, "object candidate with non-object"),
350363
};
351364

352-
let poly_trait_ref = data
365+
let object_trait_ref = data
353366
.principal()
354367
.unwrap_or_else(|| {
355368
span_bug!(obligation.cause.span, "object candidate with no principal")
@@ -361,24 +374,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
361374
let vtable_base;
362375

363376
{
364-
let tcx = self.tcx();
365-
366377
// We want to find the first supertrait in the list of
367378
// supertraits that we can unify with, and do that
368379
// unification. We know that there is exactly one in the list
369380
// where we can unify, because otherwise select would have
370381
// reported an ambiguity. (When we do find a match, also
371382
// record it for later.)
372-
let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| {
373-
match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) {
374-
Ok(obligations) => {
375-
upcast_trait_ref = Some(t);
376-
nested.extend(obligations);
377-
false
383+
let nonmatching = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref))
384+
.take_while(|&t| {
385+
match self.infcx.commit_if_ok(|_| {
386+
self.infcx
387+
.at(&obligation.cause, obligation.param_env)
388+
.sup(obligation_trait_ref, t)
389+
.map(|InferOk { obligations, .. }| obligations)
390+
.map_err(|_| ())
391+
}) {
392+
Ok(obligations) => {
393+
upcast_trait_ref = Some(t);
394+
nested.extend(obligations);
395+
false
396+
}
397+
Err(_) => true,
378398
}
379-
Err(_) => true,
380-
}
381-
});
399+
});
382400

383401
// Additionally, for each of the non-matching predicates that
384402
// we pass over, we sum up the set of number of vtable
@@ -387,47 +405,105 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
387405
vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum();
388406
}
389407

390-
for bound in data.skip_binder() {
391-
match bound {
392-
ty::ExistentialPredicate::Projection(projection) => {
393-
// This maybe belongs in wf, but that can't (doesn't) handle
394-
// higher-ranked things.
395-
// Prevent, e.g., `dyn Iterator<Item = str>`.
396-
// FIXME(generic_associated_types): We need some way to
397-
// ensure that for `dyn for<'a> X<Item<'a> = &'a ()>` the
398-
// bound holds for all `'a`.
399-
let (infer_projection, _) = self.infcx.replace_bound_vars_with_fresh_vars(
408+
// Check supertraits hold
409+
nested.extend(util::supertraits(tcx, obligation_trait_ref).skip(1).map(|super_trait| {
410+
Obligation::new(
411+
obligation.cause.clone(),
412+
obligation.param_env,
413+
super_trait.without_const().to_predicate(tcx),
414+
)
415+
}));
416+
417+
let upcast_trait_ref = upcast_trait_ref.unwrap();
418+
419+
let assoc_types: Vec<_> = tcx
420+
.associated_items(upcast_trait_ref.def_id())
421+
.in_definition_order()
422+
.filter_map(
423+
|item| if item.kind == ty::AssocKind::Type { Some(item.def_id) } else { None },
424+
)
425+
.collect();
426+
427+
if !assoc_types.is_empty() {
428+
let predicates: Vec<_> =
429+
data.iter()
430+
.filter_map(|pred| match pred {
431+
ty::ExistentialPredicate::Projection(proj) => {
432+
if assoc_types.contains(&proj.item_def_id) {
433+
match self.infcx.commit_if_ok(|_| {
434+
self.infcx
435+
.at(&obligation.cause, obligation.param_env)
436+
.sup(
437+
ty::Binder::dummy(
438+
proj.trait_ref(tcx).with_self_ty(tcx, self_ty),
439+
),
440+
upcast_trait_ref,
441+
)
442+
.map(|InferOk { obligations, .. }| obligations)
443+
.map_err(|_| ())
444+
}) {
445+
Ok(obligations) => {
446+
nested.extend(obligations);
447+
Some(proj)
448+
}
449+
Err(_) => None,
450+
}
451+
} else {
452+
None
453+
}
454+
}
455+
ty::ExistentialPredicate::AutoTrait(_)
456+
| ty::ExistentialPredicate::Trait(_) => None,
457+
})
458+
.collect();
459+
460+
let upcast_trait_ref = upcast_trait_ref
461+
.no_bound_vars()
462+
.expect("sup shouldn't return binder with bound vars");
463+
let mut normalizer = ObjectAssociatedTypeNormalizer {
464+
infcx: self.infcx,
465+
object_ty: self_ty,
466+
object_bounds: &predicates,
467+
param_env: obligation.param_env,
468+
cause: &obligation.cause,
469+
nested: &mut nested,
470+
};
471+
for assoc_type in assoc_types {
472+
if !tcx.generics_of(assoc_type).params.is_empty() {
473+
// FIXME(generic_associated_types) generate placeholders to
474+
// extend the trait substs.
475+
tcx.sess.span_fatal(
400476
obligation.cause.span,
401-
infer::HigherRankedType,
402-
&ty::Binder::bind(projection),
477+
"generic associated types in trait objects are not supported yet",
403478
);
404-
let substs: Vec<_> =
405-
iter::once(self_ty.into()).chain(infer_projection.substs).collect();
406-
let bounds =
407-
self.tcx().item_bounds(projection.item_def_id).iter().map(|bound| {
408-
// In the example above, `bound` is `<Self as Iterator>::Item: Sized`
409-
// `subst_bound` is `str: Sized`.
410-
let subst_bound = util::subst_assoc_item_bound(
411-
self.tcx(),
412-
bound,
413-
infer_projection.ty,
414-
&substs,
415-
);
416-
Obligation::new(
417-
obligation.cause.clone(),
418-
obligation.param_env.clone(),
419-
subst_bound,
420-
)
421-
});
422-
debug!("confirm_object_candidate: adding bounds: {:?}", bounds);
423-
nested.extend(bounds);
424479
}
425-
ty::ExistentialPredicate::Trait(_) | ty::ExistentialPredicate::AutoTrait(_) => {}
480+
// This maybe belongs in wf, but that can't (doesn't) handle
481+
// higher-ranked things.
482+
// Prevent, e.g., `dyn Iterator<Item = str>`.
483+
for bound in self.tcx().item_bounds(assoc_type) {
484+
let subst_bound = bound.subst(tcx, upcast_trait_ref.substs);
485+
// Normalize projections the trait object manually to
486+
// avoid evaluation overflow.
487+
let object_normalized = subst_bound.fold_with(&mut normalizer);
488+
let normalized_bound = normalize_with_depth_to(
489+
self,
490+
obligation.param_env,
491+
obligation.cause.clone(),
492+
obligation.recursion_depth + 1,
493+
&object_normalized,
494+
normalizer.nested,
495+
);
496+
normalizer.nested.push(Obligation::new(
497+
obligation.cause.clone(),
498+
obligation.param_env.clone(),
499+
normalized_bound,
500+
));
501+
}
426502
}
427503
}
428504

429505
debug!("confirm_object_candidate: nested: {:?}", nested);
430-
ImplSourceObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested }
506+
ImplSourceObjectData { upcast_trait_ref, vtable_base, nested }
431507
}
432508

433509
fn confirm_fn_pointer_candidate(
@@ -450,7 +526,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
450526
.map_bound(|(trait_ref, _)| trait_ref);
451527

452528
let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| {
453-
project::normalize_with_depth(
529+
normalize_with_depth(
454530
self,
455531
obligation.param_env,
456532
obligation.cause.clone(),
@@ -867,3 +943,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
867943
Ok(ImplSourceBuiltinData { nested })
868944
}
869945
}
946+
947+
struct ObjectAssociatedTypeNormalizer<'a, 'tcx> {
948+
infcx: &'a infer::InferCtxt<'a, 'tcx>,
949+
object_ty: Ty<'tcx>,
950+
object_bounds: &'a [ty::ExistentialProjection<'tcx>],
951+
param_env: ty::ParamEnv<'tcx>,
952+
cause: &'a ObligationCause<'tcx>,
953+
nested: &'a mut Vec<PredicateObligation<'tcx>>,
954+
}
955+
956+
impl<'tcx> TypeFolder<'tcx> for ObjectAssociatedTypeNormalizer<'_, 'tcx> {
957+
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
958+
self.infcx.tcx
959+
}
960+
961+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
962+
if !t.has_projections() {
963+
return t;
964+
}
965+
if let ty::Projection(proj) = t.kind {
966+
if let ty::Dynamic(..) = proj.self_ty().kind {
967+
for bound in self.object_bounds {
968+
if proj.item_def_id == bound.item_def_id {
969+
// FIXME(generic_associated_types): This isn't relating
970+
// the substs for the associated type.
971+
match self.infcx.commit_if_ok(|_| {
972+
self.infcx.at(self.cause, self.param_env).sub(
973+
bound
974+
.with_self_ty(self.infcx.tcx, self.object_ty)
975+
.projection_ty
976+
.trait_ref(self.infcx.tcx),
977+
proj.trait_ref(self.infcx.tcx),
978+
)
979+
}) {
980+
Ok(InferOk { value: (), obligations }) => {
981+
self.nested.extend(obligations);
982+
return bound.ty;
983+
}
984+
Err(_) => {}
985+
}
986+
}
987+
}
988+
}
989+
}
990+
t.super_fold_with(self)
991+
}
992+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Check that we validate associated type bounds on super traits for trait
2+
// objects
3+
4+
trait Super {
5+
type Y: Clone;
6+
}
7+
8+
trait X: Super {}
9+
10+
fn f<T: X + ?Sized>() {
11+
None::<T::Y>.clone();
12+
}
13+
14+
fn main() {
15+
f::<dyn X<Y = str>>();
16+
//~^ ERROR the trait bound `str: std::clone::Clone` is not satisfied
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0277]: the trait bound `str: std::clone::Clone` is not satisfied
2+
--> $DIR/check-trait-object-bounds-4.rs:15:5
3+
|
4+
LL | fn f<T: X + ?Sized>() {
5+
| - required by this bound in `f`
6+
...
7+
LL | f::<dyn X<Y = str>>();
8+
| ^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `str`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Check that we validate associated type bounds on super traits for trait
2+
// objects
3+
4+
trait Is {
5+
type T;
6+
}
7+
8+
impl<U> Is for U {
9+
type T = U;
10+
}
11+
12+
trait Super {
13+
type V;
14+
}
15+
16+
trait Obj: Super {
17+
type U: Is<T = Self::V>;
18+
}
19+
20+
fn is_obj<T: ?Sized + Obj>(_: &T) {}
21+
22+
fn f(x: &dyn Obj<U = i32, V = i64>) {
23+
is_obj(x)
24+
//~^ type mismatch resolving `<i32 as Is>::T == i64`
25+
}
26+
27+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0271]: type mismatch resolving `<i32 as Is>::T == i64`
2+
--> $DIR/check-trait-object-bounds-5.rs:23:5
3+
|
4+
LL | fn is_obj<T: ?Sized + Obj>(_: &T) {}
5+
| --- required by this bound in `is_obj`
6+
...
7+
LL | is_obj(x)
8+
| ^^^^^^ expected `i64`, found `i32`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0271`.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Check that we validate associated type bounds on super traits for trait
2+
// objects
3+
4+
trait Is {
5+
type T;
6+
}
7+
8+
impl<U> Is for U {
9+
type T = U;
10+
}
11+
12+
trait Obj {
13+
type U: Is<T = Self::V>;
14+
type V;
15+
}
16+
17+
fn is_obj<T: ?Sized + Obj>(_: &T) {}
18+
19+
fn f(x: &dyn Obj<U = i32, V = i64>) {
20+
is_obj(x)
21+
//~^ ERROR type mismatch resolving `<i32 as Is>::T == i64`
22+
}
23+
24+
fn main() {}

0 commit comments

Comments
 (0)