From 0d5a5e6059424422533775c08170e48a5104f837 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 28 Oct 2024 20:55:32 +0000 Subject: [PATCH 1/2] Disqualify built-in trait impl if it seems likely to be overlap in an unsound way with a blanket impl --- compiler/rustc_middle/src/query/mod.rs | 8 +++ compiler/rustc_middle/src/ty/context.rs | 4 ++ .../src/solve/assembly/mod.rs | 4 ++ .../rustc_trait_selection/src/traits/mod.rs | 1 + .../src/traits/project.rs | 1 + .../src/traits/select/candidate_assembly.rs | 14 +++-- .../src/traits/specialize/mod.rs | 56 +++++++++++++++++++ compiler/rustc_type_ir/src/interner.rs | 2 + compiler/rustc_type_ir/src/predicate.rs | 4 ++ ...mpl-for-trait-obj-coherence.current.stderr | 25 +++++++++ ...t-impl-for-trait-obj-coherence.next.stderr | 24 ++++++-- .../indirect-impl-for-trait-obj-coherence.rs | 4 +- .../almost-supertrait-associated-type.rs | 1 + .../almost-supertrait-associated-type.stderr | 31 ++++++++-- 14 files changed, 161 insertions(+), 18 deletions(-) create mode 100644 tests/ui/coherence/indirect-impl-for-trait-obj-coherence.current.stderr diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index fc3d690a8a9cc..83f3c8baf6d4e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1373,6 +1373,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 2841470d24878..71de428693eee 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -589,6 +589,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_def(trait_def_id).safety == hir::Safety::Unsafe } + 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 198ccb000f345..4ee0c6b1cf9d0 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 069fab6a6e6b9..63d46ec3a435c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -830,6 +830,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 49c34550f8e03..8d3d03b68d8aa 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -817,6 +817,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 5e27fd4378976..ac80b84e151d3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -832,7 +832,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; } @@ -853,9 +854,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; } @@ -879,7 +880,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(|_| { @@ -891,6 +892,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 1430cfae51f73..0a80810c85624 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::traits::Obligation; use rustc_middle::bug; @@ -595,3 +596,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 025ec7ae896dd..3ca2546a996c9 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -273,6 +273,8 @@ pub trait Interner: /// Returns `true` if this is an `unsafe trait`. fn trait_is_unsafe(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 4213ef4803be2..a3f93229a1faa 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -433,6 +433,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`. From 10da3d7580e0624378959e8b830b3a24143636d9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 21 Nov 2024 03:48:07 +0000 Subject: [PATCH 2/2] Make the implementation better --- compiler/rustc_middle/src/query/keys.rs | 8 ++ compiler/rustc_middle/src/query/mod.rs | 7 +- compiler/rustc_middle/src/ty/context.rs | 8 +- .../src/solve/assembly/mod.rs | 9 +- .../src/traits/project.rs | 7 +- .../src/traits/select/candidate_assembly.rs | 5 +- .../src/traits/specialize/mod.rs | 122 ++++++++++++++---- compiler/rustc_type_ir/src/interner.rs | 6 +- ...rect-impl-blanket-doesnt-overlap-object.rs | 25 ++++ ...ndirect-impl-blanket-downstream-trait-2.rs | 43 ++++++ ...ect-impl-blanket-downstream-trait-2.stderr | 47 +++++++ .../indirect-impl-blanket-downstream-trait.rs | 39 ++++++ ...irect-impl-blanket-downstream-trait.stderr | 22 ++++ .../coherence/overlap-dyn-impl-in-codegen.rs} | 3 +- .../overlap-dyn-impl-in-codegen.stderr | 19 +++ 15 files changed, 335 insertions(+), 35 deletions(-) create mode 100644 tests/ui/coherence/indirect-impl-blanket-doesnt-overlap-object.rs create mode 100644 tests/ui/coherence/indirect-impl-blanket-downstream-trait-2.rs create mode 100644 tests/ui/coherence/indirect-impl-blanket-downstream-trait-2.stderr create mode 100644 tests/ui/coherence/indirect-impl-blanket-downstream-trait.rs create mode 100644 tests/ui/coherence/indirect-impl-blanket-downstream-trait.stderr rename tests/{crashes/115435.rs => ui/coherence/overlap-dyn-impl-in-codegen.rs} (88%) create mode 100644 tests/ui/coherence/overlap-dyn-impl-in-codegen.stderr diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 970dc72e1ff61..76ee4e5ca0a40 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -320,6 +320,14 @@ impl Key for (DefId, SimplifiedType) { } } +impl Key for (DefId, Option) { + type Cache = DefaultCache; + + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + self.0.default_span(tcx) + } +} + impl<'tcx> Key for GenericArgsRef<'tcx> { type Cache = DefaultCache; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 83f3c8baf6d4e..62903df9c0f2e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1373,11 +1373,12 @@ 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 { + query trait_has_impl_which_may_shadow_dyn(key: (DefId, Option)) -> 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) + the built-in impl for `dyn {}`", + tcx.def_path_str(key.0), + key.1.map_or(String::from("..."), |def_id| tcx.def_path_str(def_id)), } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 71de428693eee..75fb15e810943 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -589,8 +589,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_def(trait_def_id).safety == hir::Safety::Unsafe } - 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 trait_has_impl_which_may_shadow_dyn( + self, + trait_def_id: DefId, + principal_def_id: Option, + ) -> bool { + self.trait_has_impl_which_may_shadow_dyn((trait_def_id, principal_def_id)) } fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { 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 4ee0c6b1cf9d0..6ebca80acd729 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -82,7 +82,14 @@ 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())) { + let ty::Dynamic(data, _, _) = goal.predicate.self_ty().kind() else { + unreachable!(); + }; + + if ecx.cx().trait_has_impl_which_may_shadow_dyn( + goal.predicate.trait_def_id(ecx.cx()), + data.principal_def_id(), + ) { return Err(NoSolution); } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 8d3d03b68d8aa..ba6664c70fc19 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -817,7 +817,12 @@ 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))) + .filter(|bound| { + !tcx.trait_has_impl_which_may_shadow_dyn(( + bound.trait_def_id(tcx), + data.principal_def_id(), + )) + }) .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 ac80b84e151d3..7dce065205a47 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -893,7 +893,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) }) .filter(|(_, trait_ref)| { - !tcx.trait_has_impl_which_may_shadow_dyn(trait_ref.def_id()) + !tcx.trait_has_impl_which_may_shadow_dyn(( + trait_ref.def_id(), + Some(principal_trait_ref.def_id()), + )) }) .map(|(idx, _)| ObjectCandidate(idx)); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 0a80810c85624..d874459aecd85 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -11,7 +11,7 @@ pub mod specialization_graph; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::LangItem; @@ -23,6 +23,8 @@ use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_session::lint::builtin::{COHERENCE_LEAK_CHECK, ORDER_DEPENDENT_TRAIT_OBJECTS}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, sym}; +use rustc_type_ir::elaborate; +use rustc_type_ir::fast_reject::{SimplifiedType, TreatParams, simplify_type}; use rustc_type_ir::solve::NoSolution; use specialization_graph::GraphExt; use tracing::{debug, instrument}; @@ -599,20 +601,72 @@ fn report_conflicting_impls<'tcx>( pub(super) fn trait_has_impl_which_may_shadow_dyn<'tcx>( tcx: TyCtxt<'tcx>, - trait_def_id: DefId, + (target_trait_def_id, principal_def_id): (DefId, Option), ) -> bool { // We only care about trait objects which have associated types. if !tcx - .associated_items(trait_def_id) + .associated_items(target_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 { + let target_self_ty = + principal_def_id.map_or(SimplifiedType::MarkerTraitObject, SimplifiedType::Trait); + + let elaborated_supertraits = + principal_def_id.into_iter().flat_map(|def_id| tcx.supertrait_def_ids(def_id)).collect(); + + trait_has_impl_inner( + tcx, + target_trait_def_id, + target_self_ty, + &elaborated_supertraits, + &mut Default::default(), + false, + ) +} + +fn trait_has_impl_inner<'tcx>( + tcx: TyCtxt<'tcx>, + target_trait_def_id: DefId, + target_self_ty: SimplifiedType, + elaborated_supertraits: &FxHashSet, + seen_traits: &mut FxHashSet, + for_nested_impl: bool, +) -> bool { + if tcx.is_lang_item(target_trait_def_id, LangItem::Sized) { + return false; + } + + // If we've encountered a trait in a cycle, then let's just + // consider it to be implemented defensively. + if !seen_traits.insert(target_trait_def_id) { + return true; + } + // Since we don't pass in the set of auto traits, and just the principal, + // consider all auto traits implemented. + if tcx.trait_is_auto(target_trait_def_id) { + return true; + } + // When checking the outermost call of this recursive function, we don't care + // about elaborated supertraits or built-in impls. This is intuitively because + // `dyn Trait: Trait` doesn't overlap with itself, and all built-in impls have + // `rustc_deny_explicit_impl(implement_via_object = true)`. + if for_nested_impl { + if elaborated_supertraits.contains(&target_trait_def_id) { + return true; + } + // Lazy heuristic that any lang item is potentially a built-in impl. + if tcx.as_lang_item(target_trait_def_id).is_some() { + return true; + } + } + + let mut has_offending_impl = false; + tcx.for_each_impl(target_trait_def_id, |impl_def_id| { + if has_offending_impl { return; } @@ -621,33 +675,51 @@ pub(super) fn trait_has_impl_which_may_shadow_dyn<'tcx>( .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) + if simplify_type(tcx, self_ty, TreatParams::InstantiateWithInfer) + .is_some_and(|simp| simp != target_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) + for (pred, _) in + elaborate::elaborate(tcx, tcx.predicates_of(impl_def_id).instantiate_identity(tcx)) { - return; + if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() + && trait_pred.self_ty() == self_ty + && !trait_has_impl_inner( + tcx, + trait_pred.def_id(), + target_self_ty, + elaborated_supertraits, + seen_traits, + true, + ) + { + return; + } + } + + if let ty::Alias(ty::Projection, alias_ty) = self_ty.kind() { + for pred in tcx.item_super_predicates(alias_ty.def_id).iter_identity() { + if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() + && trait_pred.self_ty() == self_ty + && !trait_has_impl_inner( + tcx, + trait_pred.def_id(), + target_self_ty, + elaborated_supertraits, + seen_traits, + false, + ) + { + return; + } + } } - has_impl = true; + has_offending_impl = true; }); - has_impl + has_offending_impl } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 3ca2546a996c9..e4119726f975a 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -273,7 +273,11 @@ pub trait Interner: /// Returns `true` if this is an `unsafe trait`. fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool; - fn trait_has_impl_which_may_shadow_dyn(self, trait_def_id: Self::DefId) -> bool; + fn trait_has_impl_which_may_shadow_dyn( + self, + trait_def_id: Self::DefId, + principal_def_id: Option, + ) -> bool; fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool; diff --git a/tests/ui/coherence/indirect-impl-blanket-doesnt-overlap-object.rs b/tests/ui/coherence/indirect-impl-blanket-doesnt-overlap-object.rs new file mode 100644 index 0000000000000..f4afb9bec4c38 --- /dev/null +++ b/tests/ui/coherence/indirect-impl-blanket-doesnt-overlap-object.rs @@ -0,0 +1,25 @@ +//@ check-pass + +// Make sure that if we don't disqualify a built-in object impl +// due to a blanket with a trait bound that will never apply to +// the object. + +pub trait SimpleService { + type Resp; +} + +trait Service { + type Resp; +} + +impl Service for S where S: SimpleService + ?Sized { + type Resp = ::Resp; +} + +fn implements_service(x: &(impl Service + ?Sized)) {} + +fn test(x: &dyn Service) { + implements_service(x); +} + +fn main() {} diff --git a/tests/ui/coherence/indirect-impl-blanket-downstream-trait-2.rs b/tests/ui/coherence/indirect-impl-blanket-downstream-trait-2.rs new file mode 100644 index 0000000000000..a4c36d627e37f --- /dev/null +++ b/tests/ui/coherence/indirect-impl-blanket-downstream-trait-2.rs @@ -0,0 +1,43 @@ +trait Trait { + type Assoc; + fn generate(&self) -> Self::Assoc; +} + +trait Other {} + +impl Trait for S where S: Other + ?Sized { + type Assoc = &'static str; + fn generate(&self) -> Self::Assoc { "hi" } +} + +trait Downstream: Trait {} +impl Other for T where T: ?Sized + Downstream + OnlyDyn {} + +trait OnlyDyn {} +impl OnlyDyn for dyn Downstream {} + +struct Concrete; +impl Trait for Concrete { + type Assoc = usize; + fn generate(&self) -> Self::Assoc { 42 } +} +impl Downstream for Concrete {} + +fn test(x: &T) { + let s: &str = x.generate(); + println!("{s}"); +} + +fn impl_downstream(x: &T) {} + +fn main() { + let x: &dyn Downstream = &Concrete; + + test(x); // This call used to segfault. + //~^ ERROR type mismatch resolving + + // This no longer holds since `Downstream: Trait`, + // but the `Trait` blanket impl now shadows. + impl_downstream(x); + //~^ ERROR type mismatch resolving +} diff --git a/tests/ui/coherence/indirect-impl-blanket-downstream-trait-2.stderr b/tests/ui/coherence/indirect-impl-blanket-downstream-trait-2.stderr new file mode 100644 index 0000000000000..95e7c1e37bab2 --- /dev/null +++ b/tests/ui/coherence/indirect-impl-blanket-downstream-trait-2.stderr @@ -0,0 +1,47 @@ +error[E0271]: type mismatch resolving `::Assoc == usize` + --> $DIR/indirect-impl-blanket-downstream-trait-2.rs:36:10 + | +LL | test(x); // This call used to segfault. + | ---- ^ type mismatch resolving `::Assoc == usize` + | | + | required by a bound introduced by this call + | +note: expected this to be `usize` + --> $DIR/indirect-impl-blanket-downstream-trait-2.rs:9:18 + | +LL | type Assoc = &'static str; + | ^^^^^^^^^^^^ +note: required for `dyn Downstream` to implement `Other` + --> $DIR/indirect-impl-blanket-downstream-trait-2.rs:14:9 + | +LL | impl Other for T where T: ?Sized + Downstream + OnlyDyn {} + | ^^^^^ ^ ---------- unsatisfied trait bound introduced here + = note: associated types for the current `impl` cannot be restricted in `where` clauses +note: required by a bound in `test` + --> $DIR/indirect-impl-blanket-downstream-trait-2.rs:26:21 + | +LL | fn test(x: &T) { + | ^^^^^ required by this bound in `test` + +error[E0271]: type mismatch resolving `::Assoc == usize` + --> $DIR/indirect-impl-blanket-downstream-trait-2.rs:41:21 + | +LL | impl_downstream(x); + | --------------- ^ type mismatch resolving `::Assoc == usize` + | | + | required by a bound introduced by this call + | +note: expected this to be `usize` + --> $DIR/indirect-impl-blanket-downstream-trait-2.rs:9:18 + | +LL | type Assoc = &'static str; + | ^^^^^^^^^^^^ +note: required by a bound in `impl_downstream` + --> $DIR/indirect-impl-blanket-downstream-trait-2.rs:31:32 + | +LL | fn impl_downstream(x: &T) {} + | ^^^^^^^^^^ required by this bound in `impl_downstream` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/coherence/indirect-impl-blanket-downstream-trait.rs b/tests/ui/coherence/indirect-impl-blanket-downstream-trait.rs new file mode 100644 index 0000000000000..bfc5bd5538572 --- /dev/null +++ b/tests/ui/coherence/indirect-impl-blanket-downstream-trait.rs @@ -0,0 +1,39 @@ +trait Trait { + type Assoc; + fn generate(&self) -> Self::Assoc; +} + +trait Other {} + +impl Trait for S where S: Other + ?Sized { + type Assoc = &'static str; + fn generate(&self) -> Self::Assoc { "hi" } +} + +trait Downstream: Trait {} +impl Other for dyn Downstream {} + +struct Concrete; +impl Trait for Concrete { + type Assoc = usize; + fn generate(&self) -> Self::Assoc { 42 } +} +impl Downstream for Concrete {} + +fn test(x: &T) { + let s: &str = x.generate(); + println!("{s}"); +} + +fn impl_downstream(x: &T) {} + +fn main() { + let x: &dyn Downstream = &Concrete; + + test(x); // This call used to segfault. + + // This no longer holds since `Downstream: Trait`, + // but the `Trait` blanket impl now shadows. + impl_downstream(x); + //~^ ERROR type mismatch resolving +} diff --git a/tests/ui/coherence/indirect-impl-blanket-downstream-trait.stderr b/tests/ui/coherence/indirect-impl-blanket-downstream-trait.stderr new file mode 100644 index 0000000000000..90f6a50405578 --- /dev/null +++ b/tests/ui/coherence/indirect-impl-blanket-downstream-trait.stderr @@ -0,0 +1,22 @@ +error[E0271]: type mismatch resolving `::Assoc == usize` + --> $DIR/indirect-impl-blanket-downstream-trait.rs:37:21 + | +LL | impl_downstream(x); + | --------------- ^ type mismatch resolving `::Assoc == usize` + | | + | required by a bound introduced by this call + | +note: expected this to be `usize` + --> $DIR/indirect-impl-blanket-downstream-trait.rs:9:18 + | +LL | type Assoc = &'static str; + | ^^^^^^^^^^^^ +note: required by a bound in `impl_downstream` + --> $DIR/indirect-impl-blanket-downstream-trait.rs:28:32 + | +LL | fn impl_downstream(x: &T) {} + | ^^^^^^^^^^ required by this bound in `impl_downstream` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/crashes/115435.rs b/tests/ui/coherence/overlap-dyn-impl-in-codegen.rs similarity index 88% rename from tests/crashes/115435.rs rename to tests/ui/coherence/overlap-dyn-impl-in-codegen.rs index c6e749867e881..a0ab970f7e358 100644 --- a/tests/crashes/115435.rs +++ b/tests/ui/coherence/overlap-dyn-impl-in-codegen.rs @@ -1,6 +1,6 @@ -//@ known-bug: #115435 //@ edition:2021 //@ compile-flags: -Copt-level=0 + trait MyTrait { type Target: ?Sized; } @@ -11,6 +11,7 @@ impl MyTrait for A { fn main() { bug_run::>(); + //~^ ERROR the size for values of type } fn bug_run() diff --git a/tests/ui/coherence/overlap-dyn-impl-in-codegen.stderr b/tests/ui/coherence/overlap-dyn-impl-in-codegen.stderr new file mode 100644 index 0000000000000..87548ef2d1c15 --- /dev/null +++ b/tests/ui/coherence/overlap-dyn-impl-in-codegen.stderr @@ -0,0 +1,19 @@ +error[E0277]: the size for values of type `dyn MyTrait` cannot be known at compilation time + --> $DIR/overlap-dyn-impl-in-codegen.rs:13:15 + | +LL | bug_run::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn MyTrait` +note: required by a bound in `bug_run` + --> $DIR/overlap-dyn-impl-in-codegen.rs:19:29 + | +LL | fn bug_run() + | ------- required by a bound in this function +LL | where +LL | ::Target: Sized, + | ^^^^^ required by this bound in `bug_run` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.