diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index e59ff02642cf1..f318c605d71e0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; @@ -59,9 +59,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - let (trait_bounds, mut projection_bounds) = + let (elaborated_trait_bounds, elaborated_projection_bounds) = traits::expand_trait_aliases(tcx, user_written_bounds.clauses()); - let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = trait_bounds + let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds .into_iter() .partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); @@ -102,30 +102,79 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + // Map the projection bounds onto a key that makes it easy to remove redundant + // bounds that are constrained by supertraits of the principal def id. + // + // Also make sure we detect conflicting bounds from expanding a trait alias and + // also specifying it manually, like: + // ``` + // type Alias = Trait; + // let _: &dyn Alias = /* ... */; + // ``` + let mut projection_bounds = FxIndexMap::default(); + for (proj, proj_span) in elaborated_projection_bounds { + let key = ( + proj.skip_binder().projection_term.def_id, + tcx.anonymize_bound_vars( + proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)), + ), + ); + if let Some((old_proj, old_proj_span)) = + projection_bounds.insert(key, (proj, proj_span)) + && tcx.anonymize_bound_vars(proj) != tcx.anonymize_bound_vars(old_proj) + { + let item = tcx.item_name(proj.item_def_id()); + self.dcx() + .struct_span_err( + span, + format!( + "conflicting associated type bounds for `{item}` when \ + expanding trait alias" + ), + ) + .with_span_label( + old_proj_span, + format!("`{item}` is specified to be `{}` here", old_proj.term()), + ) + .with_span_label( + proj_span, + format!("`{item}` is specified to be `{}` here", proj.term()), + ) + .emit(); + } + } + let principal_trait = regular_traits.into_iter().next(); - let mut needed_associated_types = FxIndexSet::default(); - if let Some((principal_trait, spans)) = &principal_trait { - let pred: ty::Predicate<'tcx> = (*principal_trait).upcast(tcx); - for ClauseWithSupertraitSpan { pred, supertrait_span } in + let mut needed_associated_types = vec![]; + if let Some((principal_trait, ref spans)) = principal_trait { + let principal_trait = principal_trait.map_bound(|trait_pred| { + assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); + trait_pred.trait_ref + }); + + for ClauseWithSupertraitSpan { clause, supertrait_span } in traits::elaborate(tcx, [ClauseWithSupertraitSpan::new( - pred, + ty::TraitRef::identity(tcx, principal_trait.def_id()).upcast(tcx), *spans.last().unwrap(), )]) .filter_only_self() { - debug!("observing object predicate `{pred:?}`"); + let clause = clause.instantiate_supertrait(tcx, principal_trait); + debug!("observing object predicate `{clause:?}`"); - let bound_predicate = pred.kind(); + let bound_predicate = clause.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + ty::ClauseKind::Trait(pred) => { // FIXME(negative_bounds): Handle this correctly... let trait_ref = tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref)); needed_associated_types.extend( - tcx.associated_items(trait_ref.def_id()) + tcx.associated_items(pred.trait_ref.def_id) .in_definition_order() + // We only care about associated types. .filter(|item| item.kind == ty::AssocKind::Type) + // No RPITITs -- even with `async_fn_in_dyn_trait`, they are implicit. .filter(|item| !item.is_impl_trait_in_trait()) // If the associated type has a `where Self: Sized` bound, // we do not need to constrain the associated type. @@ -133,7 +182,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|item| (item.def_id, trait_ref)), ); } - ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { + ty::ClauseKind::Projection(pred) => { let pred = bound_predicate.rebind(pred); // A `Self` within the original bound will be instantiated with a // `trait_object_dummy_self`, so check for that. @@ -161,8 +210,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // `dyn MyTrait`, which is uglier but works. See // the discussion in #56288 for alternatives. if !references_self { - // Include projections defined on supertraits. - projection_bounds.push((pred, supertrait_span)); + let key = ( + pred.skip_binder().projection_term.def_id, + tcx.anonymize_bound_vars( + pred.map_bound(|proj| proj.projection_term.trait_ref(tcx)), + ), + ); + if !projection_bounds.contains_key(&key) { + projection_bounds.insert(key, (pred, supertrait_span)); + } } self.check_elaborated_projection_mentions_input_lifetimes( @@ -182,12 +238,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // types that we expect to be provided by the user, so the following loop // removes all the associated types that have a corresponding `Projection` // clause, either from expanding trait aliases or written by the user. - for &(projection_bound, span) in &projection_bounds { + for &(projection_bound, span) in projection_bounds.values() { let def_id = projection_bound.item_def_id(); - let trait_ref = tcx.anonymize_bound_vars( - projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)), - ); - needed_associated_types.swap_remove(&(def_id, trait_ref)); if tcx.generics_require_sized_self(def_id) { tcx.emit_node_span_lint( UNUSED_ASSOCIATED_TYPE_BOUNDS, @@ -198,9 +250,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + let mut missing_assoc_types = FxIndexSet::default(); + let projection_bounds: Vec<_> = needed_associated_types + .into_iter() + .filter_map(|key| { + if let Some(assoc) = projection_bounds.get(&key) { + Some(*assoc) + } else { + missing_assoc_types.insert(key); + None + } + }) + .collect(); + if let Err(guar) = self.check_for_required_assoc_tys( principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()), - needed_associated_types, + missing_assoc_types, potential_assoc_types, hir_bounds, ) { @@ -266,7 +331,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) }); - let existential_projections = projection_bounds.iter().map(|(bound, _)| { + let existential_projections = projection_bounds.into_iter().map(|(bound, _)| { bound.map_bound(|mut b| { assert_eq!(b.projection_term.self_ty(), dummy_self); @@ -291,12 +356,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) }); - let auto_trait_predicates = auto_traits.into_iter().map(|(trait_pred, _)| { - assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive); - assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self); + let mut auto_trait_predicates: Vec<_> = auto_traits + .into_iter() + .map(|(trait_pred, _)| { + assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive); + assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self); - ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id())) - }); + ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id())) + }) + .collect(); + auto_trait_predicates.dedup(); // N.b. principal, projections, auto traits // FIXME: This is actually wrong with multiple principals in regards to symbol mangling @@ -306,7 +375,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .chain(auto_trait_predicates) .collect::>(); v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); - v.dedup(); let existential_predicates = tcx.mk_poly_existential_predicates(&v); // Use explicitly-specified region bound, unless the bound is missing. diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index afdec7a86d445..1d1aad916bc21 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -79,20 +79,11 @@ impl<'tcx> Relate> for &'tcx ty::List RelateResult<'tcx, Self> { let tcx = relation.cx(); - - // FIXME: this is wasteful, but want to do a perf run to see how slow it is. - // We need to perform this deduplication as we sometimes generate duplicate projections - // in `a`. - let mut a_v: Vec<_> = a.into_iter().collect(); - let mut b_v: Vec<_> = b.into_iter().collect(); - a_v.dedup(); - b_v.dedup(); - if a_v.len() != b_v.len() { + if a.len() != b.len() { return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))); } - - let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| { - match (ep_a.skip_binder(), ep_b.skip_binder()) { + let v = + iter::zip(a, b).map(|(ep_a, ep_b)| match (ep_a.skip_binder(), ep_b.skip_binder()) { (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => { Ok(ep_a.rebind(ty::ExistentialPredicate::Trait( relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), @@ -109,8 +100,7 @@ impl<'tcx> Relate> for &'tcx ty::List Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))), _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))), - } - }); + }); tcx.mk_poly_existential_predicates_from_iter(v) } } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 923b74abdfd2f..b11bcff1d8bbb 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -44,25 +44,22 @@ pub trait Elaboratable { } pub struct ClauseWithSupertraitSpan { - pub pred: I::Predicate, + pub clause: I::Clause, // Span of the supertrait predicatae that lead to this clause. pub supertrait_span: I::Span, } impl ClauseWithSupertraitSpan { - pub fn new(pred: I::Predicate, span: I::Span) -> Self { - ClauseWithSupertraitSpan { pred, supertrait_span: span } + pub fn new(clause: I::Clause, span: I::Span) -> Self { + ClauseWithSupertraitSpan { clause, supertrait_span: span } } } impl Elaboratable for ClauseWithSupertraitSpan { fn predicate(&self) -> ::Predicate { - self.pred + self.clause.as_predicate() } fn child(&self, clause: ::Clause) -> Self { - ClauseWithSupertraitSpan { - pred: clause.as_predicate(), - supertrait_span: self.supertrait_span, - } + ClauseWithSupertraitSpan { clause, supertrait_span: self.supertrait_span } } fn child_with_derived_cause( @@ -72,7 +69,7 @@ impl Elaboratable for ClauseWithSupertraitSpan { _parent_trait_pred: crate::Binder>, _index: usize, ) -> Self { - ClauseWithSupertraitSpan { pred: clause.as_predicate(), supertrait_span } + ClauseWithSupertraitSpan { clause, supertrait_span } } } diff --git a/tests/ui/associated-types/associated-types-overridden-binding-2.rs b/tests/ui/associated-types/associated-types-overridden-binding-2.rs index fed60ccf089d0..247724eaaf112 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding-2.rs +++ b/tests/ui/associated-types/associated-types-overridden-binding-2.rs @@ -4,5 +4,5 @@ trait I32Iterator = Iterator; fn main() { let _: &dyn I32Iterator = &vec![42].into_iter(); - //~^ ERROR expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` + //~^ ERROR conflicting associated type bounds } diff --git a/tests/ui/associated-types/associated-types-overridden-binding-2.stderr b/tests/ui/associated-types/associated-types-overridden-binding-2.stderr index 4dfd275a19058..71a4a2610aac4 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding-2.stderr +++ b/tests/ui/associated-types/associated-types-overridden-binding-2.stderr @@ -1,11 +1,13 @@ -error[E0271]: expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` - --> $DIR/associated-types-overridden-binding-2.rs:6:43 +error: conflicting associated type bounds for `Item` when expanding trait alias + --> $DIR/associated-types-overridden-binding-2.rs:6:13 | +LL | trait I32Iterator = Iterator; + | ---------- `Item` is specified to be `i32` here +... LL | let _: &dyn I32Iterator = &vec![42].into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` - | - = note: required for the cast from `&std::vec::IntoIter` to `&dyn Iterator` + | ^^^^^^^^^^^^^^^^----------^ + | | + | `Item` is specified to be `u32` here error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/associated-types/associated-types-overridden-binding.rs b/tests/ui/associated-types/associated-types-overridden-binding.rs index 9a64a06c31bad..333a3e30c7dcf 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding.rs +++ b/tests/ui/associated-types/associated-types-overridden-binding.rs @@ -8,4 +8,5 @@ trait U32Iterator = I32Iterator; //~ ERROR type annotations needed fn main() { let _: &dyn I32Iterator; + //~^ ERROR conflicting associated type bounds } diff --git a/tests/ui/associated-types/associated-types-overridden-binding.stderr b/tests/ui/associated-types/associated-types-overridden-binding.stderr index dc087e4185fb6..3b20015dfcab3 100644 --- a/tests/ui/associated-types/associated-types-overridden-binding.stderr +++ b/tests/ui/associated-types/associated-types-overridden-binding.stderr @@ -22,6 +22,17 @@ note: required by a bound in `I32Iterator` LL | trait I32Iterator = Iterator; | ^^^^^^^^^^ required by this bound in `I32Iterator` -error: aborting due to 2 previous errors +error: conflicting associated type bounds for `Item` when expanding trait alias + --> $DIR/associated-types-overridden-binding.rs:10:13 + | +LL | trait I32Iterator = Iterator; + | ---------- `Item` is specified to be `i32` here +... +LL | let _: &dyn I32Iterator; + | ^^^^^^^^^^^^^^^^----------^ + | | + | `Item` is specified to be `u32` here + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/closures/deduce-from-object-supertrait.rs b/tests/ui/closures/deduce-from-object-supertrait.rs new file mode 100644 index 0000000000000..315d90c4c5086 --- /dev/null +++ b/tests/ui/closures/deduce-from-object-supertrait.rs @@ -0,0 +1,15 @@ +//@ check-pass + +trait Foo: Fn(Bar) {} +impl Foo for T where T: Fn(Bar) {} + +struct Bar; +impl Bar { + fn bar(&self) {} +} + +fn main() { + let x: &dyn Foo = &|x| { + x.bar(); + }; +} diff --git a/tests/ui/dyn-compatibility/multiple-supers-should-work.rs b/tests/ui/dyn-compatibility/multiple-supers-should-work.rs new file mode 100644 index 0000000000000..355089b9a9d89 --- /dev/null +++ b/tests/ui/dyn-compatibility/multiple-supers-should-work.rs @@ -0,0 +1,18 @@ +//@ check-pass + +trait Sup { + type Assoc; +} + +impl Sup for () { + type Assoc = T; +} + +trait Trait: Sup + Sup {} + +impl Trait for () {} + +fn main() { + let x: &dyn Trait<(), _> = &(); + let y: &dyn Trait<_, ()> = x; +} diff --git a/tests/ui/traits/object/crash-due-to-projections-modulo-norm.rs b/tests/ui/traits/object/crash-due-to-projections-modulo-norm.rs new file mode 100644 index 0000000000000..025fb166254d2 --- /dev/null +++ b/tests/ui/traits/object/crash-due-to-projections-modulo-norm.rs @@ -0,0 +1,41 @@ +//@ check-pass + +// Step 1: Create two names for a single type: `Thing` and `AlsoThing` + +struct Thing; +struct Dummy; +trait DummyTrait { + type DummyType; +} +impl DummyTrait for Dummy { + type DummyType = Thing; +} +type AlsoThing = ::DummyType; + +// Step 2: Create names for a single trait object type: `TraitObject` and `AlsoTraitObject` + +trait SomeTrait { + type Item; +} +type TraitObject = dyn SomeTrait; +type AlsoTraitObject = dyn SomeTrait; + +// Step 3: Force the compiler to check whether the two names are the same type + +trait Supertrait { + type Foo; +} +trait Subtrait: Supertrait {} + +trait HasOutput { + type Output; +} + +fn foo() -> F::Output +where + F: HasOutput>, +{ + todo!() +} + +fn main() {} diff --git a/tests/ui/traits/object/incomplete-multiple-super-projection.rs b/tests/ui/traits/object/incomplete-multiple-super-projection.rs new file mode 100644 index 0000000000000..c9f099b9a36de --- /dev/null +++ b/tests/ui/traits/object/incomplete-multiple-super-projection.rs @@ -0,0 +1,30 @@ +trait Sup { + type Assoc; +} + +impl Sup for () { + type Assoc = T; +} +impl Dyn for () {} + +trait Dyn: Sup + Sup {} + +trait Trait { + type Assoc; +} +impl Trait for dyn Dyn<(), ()> { + type Assoc = &'static str; +} +impl Trait for dyn Dyn { +//~^ ERROR conflicting implementations of trait `Trait` for type `(dyn Dyn<(), ()> + 'static)` + type Assoc = usize; +} + +fn call(x: usize) -> as Trait>::Assoc { + x +} + +fn main() { + let x: &'static str = call::<(), ()>(0xDEADBEEF); + println!("{x}"); +} diff --git a/tests/ui/traits/object/incomplete-multiple-super-projection.stderr b/tests/ui/traits/object/incomplete-multiple-super-projection.stderr new file mode 100644 index 0000000000000..90ad9e3272186 --- /dev/null +++ b/tests/ui/traits/object/incomplete-multiple-super-projection.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `(dyn Dyn<(), ()> + 'static)` + --> $DIR/incomplete-multiple-super-projection.rs:18:1 + | +LL | impl Trait for dyn Dyn<(), ()> { + | ------------------------------ first implementation here +... +LL | impl Trait for dyn Dyn { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Dyn<(), ()> + 'static)` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/object/infer-shadows-implied-projection.rs b/tests/ui/traits/object/infer-shadows-implied-projection.rs new file mode 100644 index 0000000000000..628912c54faaa --- /dev/null +++ b/tests/ui/traits/object/infer-shadows-implied-projection.rs @@ -0,0 +1,19 @@ +//@ check-pass + +trait Database: Restriction {} + +trait Restriction { + type Inner; +} + +struct Test {} + +impl Database for Test {} +impl Restriction for Test { + type Inner = u32; +} + +fn main() { + let t = Test {}; + let x: &dyn Database = &t; +} diff --git a/tests/ui/traits/object/outlives-super-proj.rs b/tests/ui/traits/object/outlives-super-proj.rs new file mode 100644 index 0000000000000..15b67d9ab68e7 --- /dev/null +++ b/tests/ui/traits/object/outlives-super-proj.rs @@ -0,0 +1,24 @@ +//@ check-pass + +// Make sure that we still deduce outlives bounds from supertrait projections +// and require them for well-formedness. + +trait Trait { + type Assoc; +} + +trait Bar { + type Assoc; +} + +trait Foo<'a, T: 'a>: Bar { + +} + +fn outlives<'a, T: 'a>() {} + +fn implied_outlives<'a, T: Trait>(x: &dyn Foo<'a, T::Assoc>) { + outlives::<'a, T::Assoc>(); +} + +fn main() {} diff --git a/tests/ui/traits/object/pretty.stderr b/tests/ui/traits/object/pretty.stderr index 2f9fdf151f08c..37fe142951d89 100644 --- a/tests/ui/traits/object/pretty.stderr +++ b/tests/ui/traits/object/pretty.stderr @@ -154,12 +154,12 @@ error[E0308]: mismatched types --> $DIR/pretty.rs:41:56 | LL | fn dyn_has_gat(x: &dyn HasGat = ()>) { x } - | - ^ expected `()`, found `&dyn HasGat = ()>` + | - ^ expected `()`, found `&dyn HasGat` | | - | help: try adding a return type: `-> &dyn HasGat = ()>` + | help: try adding a return type: `-> &dyn HasGat` | = note: expected unit type `()` - found reference `&dyn HasGat = ()>` + found reference `&dyn HasGat` error: aborting due to 14 previous errors; 1 warning emitted diff --git a/tests/ui/traits/object/redundant.rs b/tests/ui/traits/object/redundant.rs new file mode 100644 index 0000000000000..be07b15713898 --- /dev/null +++ b/tests/ui/traits/object/redundant.rs @@ -0,0 +1,12 @@ +//@ check-pass + +trait Foo: Bar {} +trait Bar { + type Out; +} + +fn w(x: &dyn Foo) { + let x: &dyn Foo = x; +} + +fn main() {}