diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d7a60a843b717..ac0a3ed638281 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1371,6 +1371,14 @@ rustc_queries! { desc { |tcx| "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) } } + query trait_has_impl_which_may_shadow_dyn(trait_def_id: DefId) -> bool { + desc { + |tcx| "checking if trait `{}` has an impl which may overlap with \ + the built-in impl for trait objects", + tcx.def_path_str(trait_def_id) + } + } + /// Gets the ParameterEnvironment for a given item; this environment /// will be in "user-facing" mode, meaning that it is suitable for /// type-checking etc, and it does not normalize specializable diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5ae9c589ec4a5..9d04f014e9f60 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -581,6 +581,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_def(trait_def_id).implement_via_object } + fn trait_has_impl_which_may_shadow_dyn(self, trait_def_id: DefId) -> bool { + self.trait_has_impl_which_may_shadow_dyn(trait_def_id) + } + fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { self.is_impl_trait_in_trait(def_id) } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index f6a5f20a639ec..3abc92e68fd56 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -82,6 +82,10 @@ where goal: Goal, assumption: I::Clause, ) -> Result, NoSolution> { + if ecx.cx().trait_has_impl_which_may_shadow_dyn(goal.predicate.trait_def_id(ecx.cx())) { + return Err(NoSolution); + } + Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { let cx = ecx.cx(); let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index cdf24887e763b..1a2f8358298a1 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -601,6 +601,7 @@ pub fn provide(providers: &mut Providers) { specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, specialization_enabled_in: specialize::specialization_enabled_in, + trait_has_impl_which_may_shadow_dyn: specialize::trait_has_impl_which_may_shadow_dyn, instantiate_and_check_impossible_predicates, is_impossible_associated_item, ..*providers diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 0803dd74b89bb..b1955068cd824 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -864,6 +864,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( let env_predicates = data .projection_bounds() .filter(|bound| bound.item_def_id() == obligation.predicate.def_id) + .filter(|bound| !tcx.trait_has_impl_which_may_shadow_dyn(bound.trait_def_id(tcx))) .map(|p| p.with_self_ty(tcx, object_ty).upcast(tcx)); assemble_candidates_from_predicates( diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e027586563ecc..6c39b52f38320 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -855,7 +855,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { "assemble_candidates_from_object_ty", ); - if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object { + let tcx = self.tcx(); + if !tcx.trait_def(obligation.predicate.def_id()).implement_via_object { return; } @@ -876,9 +877,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Some(principal) = data.principal() { if !self.infcx.tcx.features().dyn_compatible_for_dispatch() { - principal.with_self_ty(self.tcx(), self_ty) - } else if self.tcx().is_dyn_compatible(principal.def_id()) { - principal.with_self_ty(self.tcx(), self_ty) + principal.with_self_ty(tcx, self_ty) + } else if tcx.is_dyn_compatible(principal.def_id()) { + principal.with_self_ty(tcx, self_ty) } else { return; } @@ -902,7 +903,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // correct trait, but also the correct type parameters. // For example, we may be trying to upcast `Foo` to `Bar`, // but `Foo` is declared as `trait Foo: Bar`. - let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref) + let candidate_supertraits = util::supertraits(tcx, principal_trait_ref) .enumerate() .filter(|&(_, upcast_trait_ref)| { self.infcx.probe(|_| { @@ -914,6 +915,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .is_ok() }) }) + .filter(|(_, trait_ref)| { + !tcx.trait_has_impl_which_may_shadow_dyn(trait_ref.def_id()) + }) .map(|(idx, _)| ObjectCandidate(idx)); candidates.vec.extend(candidate_supertraits); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 0e45f7a195f04..44173493d0903 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -14,6 +14,7 @@ pub mod specialization_graph; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Diag, EmissionGuarantee}; +use rustc_hir::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::bug; @@ -478,3 +479,58 @@ fn report_conflicting_impls<'tcx>( } } } + +pub(super) fn trait_has_impl_which_may_shadow_dyn<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, +) -> bool { + // We only care about trait objects which have associated types. + if !tcx + .associated_items(trait_def_id) + .in_definition_order() + .any(|item| item.kind == ty::AssocKind::Type) + { + return false; + } + + let mut has_impl = false; + tcx.for_each_impl(trait_def_id, |impl_def_id| { + if has_impl { + return; + } + + let self_ty = tcx + .impl_trait_ref(impl_def_id) + .expect("impl must have trait ref") + .instantiate_identity() + .self_ty(); + if self_ty.is_known_rigid() { + return; + } + + let sized_trait = tcx.require_lang_item(LangItem::Sized, None); + if tcx + .param_env(impl_def_id) + .caller_bounds() + .iter() + .filter_map(|clause| clause.as_trait_clause()) + .any(|bound| bound.def_id() == sized_trait && bound.self_ty().skip_binder() == self_ty) + { + return; + } + + if let ty::Alias(ty::Projection, alias_ty) = self_ty.kind() + && tcx + .item_super_predicates(alias_ty.def_id) + .iter_identity() + .filter_map(|clause| clause.as_trait_clause()) + .any(|bound| bound.def_id() == sized_trait) + { + return; + } + + has_impl = true; + }); + + has_impl +} diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 6a8113b38b7bd..d00d68f07410b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -274,6 +274,8 @@ pub trait Interner: fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; + fn trait_has_impl_which_may_shadow_dyn(self, trait_def_id: Self::DefId) -> bool; + fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool; fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index c316455034873..3a8010d39e5b1 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -434,6 +434,10 @@ impl ty::Binder> { pub fn item_def_id(&self) -> I::DefId { self.skip_binder().def_id } + + pub fn trait_def_id(self, interner: I) -> I::DefId { + interner.parent(self.skip_binder().def_id) + } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] diff --git a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.current.stderr b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.current.stderr new file mode 100644 index 0000000000000..1884193e53da5 --- /dev/null +++ b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.current.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/indirect-impl-for-trait-obj-coherence.rs:23:41 + | +LL | fn transmute(x: T) -> U { + | - - expected type parameter + | | + | found type parameter +LL | foo::, U>(x) + | ----------------------------------- ^ expected type parameter `U`, found type parameter `T` + | | + | arguments to this function are incorrect + | + = note: expected type parameter `U` + found type parameter `T` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters +note: function defined here + --> $DIR/indirect-impl-for-trait-obj-coherence.rs:17:4 + | +LL | fn foo(x: >::Output) -> U { + | ^^^ --------------------------- + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr index b6636d4de86eb..1884193e53da5 100644 --- a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr +++ b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr @@ -1,9 +1,25 @@ -error[E0284]: type annotations needed: cannot normalize ` as Object>::Output` - --> $DIR/indirect-impl-for-trait-obj-coherence.rs:25:41 +error[E0308]: mismatched types + --> $DIR/indirect-impl-for-trait-obj-coherence.rs:23:41 | +LL | fn transmute(x: T) -> U { + | - - expected type parameter + | | + | found type parameter LL | foo::, U>(x) - | ^ cannot normalize ` as Object>::Output` + | ----------------------------------- ^ expected type parameter `U`, found type parameter `T` + | | + | arguments to this function are incorrect + | + = note: expected type parameter `U` + found type parameter `T` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters +note: function defined here + --> $DIR/indirect-impl-for-trait-obj-coherence.rs:17:4 + | +LL | fn foo(x: >::Output) -> U { + | ^^^ --------------------------- error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0284`. +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs index abfd51c2008bb..5f1d7df7d85d5 100644 --- a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs +++ b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs @@ -1,8 +1,6 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@[current] check-pass -//@[current] known-bug: #57893 // Should fail. Because we see an impl that uses a certain associated type, we // type-check assuming that impl is used. However, this conflicts with the @@ -23,7 +21,7 @@ fn foo(x: >::Output) -> U { #[allow(dead_code)] fn transmute(x: T) -> U { foo::, U>(x) - //[next]~^ ERROR type annotations needed: cannot normalize ` as Object>::Output` + //~^ ERROR mismatched types } fn main() {} diff --git a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs index 83076f7d5fc3b..4fc93051ba9df 100644 --- a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs +++ b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs @@ -7,6 +7,7 @@ fn transmute(t: T) -> U { (&PhantomData:: as &dyn Foo).transmute(t) //~^ ERROR the trait `Foo` cannot be made into an object //~| ERROR the trait `Foo` cannot be made into an object + //~| ERROR the trait bound `dyn Foo: Super` is not satisfied } struct ActuallySuper; diff --git a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr index 99bcccc20c01a..ec33d12356fe7 100644 --- a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr +++ b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr @@ -1,11 +1,11 @@ error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/almost-supertrait-associated-type.rs:21:20 + --> $DIR/almost-supertrait-associated-type.rs:22:20 | LL | impl Dyn for dyn Foo + '_ { | ^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/almost-supertrait-associated-type.rs:33:34 + --> $DIR/almost-supertrait-associated-type.rs:34:34 | LL | trait Foo: Super | --- this trait cannot be made into an object... @@ -15,6 +15,24 @@ LL | fn transmute(&self, t: T) -> >::Assoc; = help: consider moving `transmute` to another trait = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead +error[E0277]: the trait bound `dyn Foo: Super` is not satisfied + --> $DIR/almost-supertrait-associated-type.rs:7:43 + | +LL | (&PhantomData:: as &dyn Foo).transmute(t) + | ^^^^^^^^^ the trait `Super` is not implemented for `dyn Foo` + | + = help: the following other types implement trait `Super`: + `PhantomData` implements `Super` + `PhantomData` implements `Super` +note: required by a bound in `Foo::transmute` + --> $DIR/almost-supertrait-associated-type.rs:30:18 + | +LL | trait Foo: Super + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::transmute` +... +LL | fn transmute(&self, t: T) -> >::Assoc; + | --------- required by a bound in this associated function + error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/almost-supertrait-associated-type.rs:7:27 | @@ -22,7 +40,7 @@ LL | (&PhantomData:: as &dyn Foo).transmute(t) | ^^^^^^^^^^^^^^ `Foo` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/almost-supertrait-associated-type.rs:33:34 + --> $DIR/almost-supertrait-associated-type.rs:34:34 | LL | trait Foo: Super | --- this trait cannot be made into an object... @@ -39,7 +57,7 @@ LL | (&PhantomData:: as &dyn Foo).transmute(t) | ^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object | note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/almost-supertrait-associated-type.rs:33:34 + --> $DIR/almost-supertrait-associated-type.rs:34:34 | LL | trait Foo: Super | --- this trait cannot be made into an object... @@ -50,6 +68,7 @@ LL | fn transmute(&self, t: T) -> >::Assoc; = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead = note: required for the cast from `&PhantomData` to `&dyn Foo` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0038`. +Some errors have detailed explanations: E0038, E0277. +For more information about an error, try `rustc --explain E0038`.