Skip to content

Commit

Permalink
Auto merge of rust-lang#132289 - compiler-errors:vanquish-dyn-incompl…
Browse files Browse the repository at this point in the history
…eteness, r=<try>

Disqualify built-in trait impl if it seems likely to be overlap in an unsound way with a blanket impl

r? lcnr
  • Loading branch information
bors committed Oct 28, 2024
2 parents 3f1be1e + 3ee780b commit 264a7c4
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 18 deletions.
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ where
goal: Goal<I, Self>,
assumption: I::Clause,
) -> Result<Candidate<I>, 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 {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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;
}
Expand All @@ -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<i32>`,
// but `Foo` is declared as `trait Foo: Bar<u32>`.
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(|_| {
Expand All @@ -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);
Expand Down
56 changes: 56 additions & 0 deletions compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}
2 changes: 2 additions & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_type_ir/src/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@ impl<I: Interner> ty::Binder<I, ExistentialProjection<I>> {
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)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error[E0308]: mismatched types
--> $DIR/indirect-impl-for-trait-obj-coherence.rs:23:41
|
LL | fn transmute<T, U>(x: T) -> U {
| - - expected type parameter
| |
| found type parameter
LL | foo::<dyn Object<U, Output = T>, 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<T: ?Sized, U>(x: <T as Object<U>>::Output) -> U {
| ^^^ ---------------------------

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
error[E0284]: type annotations needed: cannot normalize `<dyn Object<U, Output = T> as Object<U>>::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<T, U>(x: T) -> U {
| - - expected type parameter
| |
| found type parameter
LL | foo::<dyn Object<U, Output = T>, U>(x)
| ^ cannot normalize `<dyn Object<U, Output = T> as Object<U>>::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<T: ?Sized, U>(x: <T as Object<U>>::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`.
4 changes: 1 addition & 3 deletions tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -23,7 +21,7 @@ fn foo<T: ?Sized, U>(x: <T as Object<U>>::Output) -> U {
#[allow(dead_code)]
fn transmute<T, U>(x: T) -> U {
foo::<dyn Object<U, Output = T>, U>(x)
//[next]~^ ERROR type annotations needed: cannot normalize `<dyn Object<U, Output = T> as Object<U>>::Output`
//~^ ERROR mismatched types
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ fn transmute<T, U>(t: T) -> U {
(&PhantomData::<T> as &dyn Foo<T, U>).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<T, U>: Super<ActuallySuper>` is not satisfied
}

struct ActuallySuper;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T, U> Dyn for dyn Foo<T, U> + '_ {
| ^^^^^^^^^^^^^^^^^^ `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 <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> $DIR/almost-supertrait-associated-type.rs:33:34
--> $DIR/almost-supertrait-associated-type.rs:34:34
|
LL | trait Foo<T, U>: Super<ActuallySuper, Assoc = T>
| --- this trait cannot be made into an object...
Expand All @@ -15,14 +15,32 @@ LL | fn transmute(&self, t: T) -> <Self as Super<NotActuallySuper>>::Assoc;
= help: consider moving `transmute` to another trait
= help: only type `std::marker::PhantomData<T>` implements the trait, consider using it directly instead

error[E0277]: the trait bound `dyn Foo<T, U>: Super<ActuallySuper>` is not satisfied
--> $DIR/almost-supertrait-associated-type.rs:7:43
|
LL | (&PhantomData::<T> as &dyn Foo<T, U>).transmute(t)
| ^^^^^^^^^ the trait `Super<ActuallySuper>` is not implemented for `dyn Foo<T, U>`
|
= help: the following other types implement trait `Super<Q>`:
`PhantomData<T>` implements `Super<ActuallySuper>`
`PhantomData<T>` implements `Super<NotActuallySuper>`
note: required by a bound in `Foo::transmute`
--> $DIR/almost-supertrait-associated-type.rs:30:18
|
LL | trait Foo<T, U>: Super<ActuallySuper, Assoc = T>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::transmute`
...
LL | fn transmute(&self, t: T) -> <Self as Super<NotActuallySuper>>::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
|
LL | (&PhantomData::<T> as &dyn Foo<T, U>).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 <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> $DIR/almost-supertrait-associated-type.rs:33:34
--> $DIR/almost-supertrait-associated-type.rs:34:34
|
LL | trait Foo<T, U>: Super<ActuallySuper, Assoc = T>
| --- this trait cannot be made into an object...
Expand All @@ -39,7 +57,7 @@ LL | (&PhantomData::<T> as &dyn Foo<T, U>).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 <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> $DIR/almost-supertrait-associated-type.rs:33:34
--> $DIR/almost-supertrait-associated-type.rs:34:34
|
LL | trait Foo<T, U>: Super<ActuallySuper, Assoc = T>
| --- this trait cannot be made into an object...
Expand All @@ -50,6 +68,7 @@ LL | fn transmute(&self, t: T) -> <Self as Super<NotActuallySuper>>::Assoc;
= help: only type `std::marker::PhantomData<T>` implements the trait, consider using it directly instead
= note: required for the cast from `&PhantomData<T>` to `&dyn Foo<T, U>`

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`.

0 comments on commit 264a7c4

Please sign in to comment.