Skip to content

Commit 0d5a5e6

Browse files
Disqualify built-in trait impl if it seems likely to be overlap in an unsound way with a blanket impl
1 parent 33c245b commit 0d5a5e6

File tree

14 files changed

+161
-18
lines changed

14 files changed

+161
-18
lines changed

compiler/rustc_middle/src/query/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,14 @@ rustc_queries! {
13731373
desc { |tcx| "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) }
13741374
}
13751375

1376+
query trait_has_impl_which_may_shadow_dyn(trait_def_id: DefId) -> bool {
1377+
desc {
1378+
|tcx| "checking if trait `{}` has an impl which may overlap with \
1379+
the built-in impl for trait objects",
1380+
tcx.def_path_str(trait_def_id)
1381+
}
1382+
}
1383+
13761384
/// Gets the ParameterEnvironment for a given item; this environment
13771385
/// will be in "user-facing" mode, meaning that it is suitable for
13781386
/// type-checking etc, and it does not normalize specializable

compiler/rustc_middle/src/ty/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
589589
self.trait_def(trait_def_id).safety == hir::Safety::Unsafe
590590
}
591591

592+
fn trait_has_impl_which_may_shadow_dyn(self, trait_def_id: DefId) -> bool {
593+
self.trait_has_impl_which_may_shadow_dyn(trait_def_id)
594+
}
595+
592596
fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
593597
self.is_impl_trait_in_trait(def_id)
594598
}

compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ where
8282
goal: Goal<I, Self>,
8383
assumption: I::Clause,
8484
) -> Result<Candidate<I>, NoSolution> {
85+
if ecx.cx().trait_has_impl_which_may_shadow_dyn(goal.predicate.trait_def_id(ecx.cx())) {
86+
return Err(NoSolution);
87+
}
88+
8589
Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
8690
let cx = ecx.cx();
8791
let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else {

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ pub fn provide(providers: &mut Providers) {
830830
specialization_graph_of: specialize::specialization_graph_provider,
831831
specializes: specialize::specializes,
832832
specialization_enabled_in: specialize::specialization_enabled_in,
833+
trait_has_impl_which_may_shadow_dyn: specialize::trait_has_impl_which_may_shadow_dyn,
833834
instantiate_and_check_impossible_predicates,
834835
is_impossible_associated_item,
835836
..*providers

compiler/rustc_trait_selection/src/traits/project.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
817817
let env_predicates = data
818818
.projection_bounds()
819819
.filter(|bound| bound.item_def_id() == obligation.predicate.def_id)
820+
.filter(|bound| !tcx.trait_has_impl_which_may_shadow_dyn(bound.trait_def_id(tcx)))
820821
.map(|p| p.with_self_ty(tcx, object_ty).upcast(tcx));
821822

822823
assemble_candidates_from_predicates(

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
832832
"assemble_candidates_from_object_ty",
833833
);
834834

835-
if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object {
835+
let tcx = self.tcx();
836+
if !tcx.trait_def(obligation.predicate.def_id()).implement_via_object {
836837
return;
837838
}
838839

@@ -853,9 +854,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
853854

854855
if let Some(principal) = data.principal() {
855856
if !self.infcx.tcx.features().dyn_compatible_for_dispatch() {
856-
principal.with_self_ty(self.tcx(), self_ty)
857-
} else if self.tcx().is_dyn_compatible(principal.def_id()) {
858-
principal.with_self_ty(self.tcx(), self_ty)
857+
principal.with_self_ty(tcx, self_ty)
858+
} else if tcx.is_dyn_compatible(principal.def_id()) {
859+
principal.with_self_ty(tcx, self_ty)
859860
} else {
860861
return;
861862
}
@@ -879,7 +880,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
879880
// correct trait, but also the correct type parameters.
880881
// For example, we may be trying to upcast `Foo` to `Bar<i32>`,
881882
// but `Foo` is declared as `trait Foo: Bar<u32>`.
882-
let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref)
883+
let candidate_supertraits = util::supertraits(tcx, principal_trait_ref)
883884
.enumerate()
884885
.filter(|&(_, upcast_trait_ref)| {
885886
self.infcx.probe(|_| {
@@ -891,6 +892,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
891892
.is_ok()
892893
})
893894
})
895+
.filter(|(_, trait_ref)| {
896+
!tcx.trait_has_impl_which_may_shadow_dyn(trait_ref.def_id())
897+
})
894898
.map(|(idx, _)| ObjectCandidate(idx));
895899

896900
candidates.vec.extend(candidate_supertraits);

compiler/rustc_trait_selection/src/traits/specialize/mod.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub mod specialization_graph;
1414
use rustc_data_structures::fx::FxIndexSet;
1515
use rustc_errors::codes::*;
1616
use rustc_errors::{Diag, EmissionGuarantee};
17+
use rustc_hir::LangItem;
1718
use rustc_hir::def_id::{DefId, LocalDefId};
1819
use rustc_infer::traits::Obligation;
1920
use rustc_middle::bug;
@@ -595,3 +596,58 @@ fn report_conflicting_impls<'tcx>(
595596
}
596597
}
597598
}
599+
600+
pub(super) fn trait_has_impl_which_may_shadow_dyn<'tcx>(
601+
tcx: TyCtxt<'tcx>,
602+
trait_def_id: DefId,
603+
) -> bool {
604+
// We only care about trait objects which have associated types.
605+
if !tcx
606+
.associated_items(trait_def_id)
607+
.in_definition_order()
608+
.any(|item| item.kind == ty::AssocKind::Type)
609+
{
610+
return false;
611+
}
612+
613+
let mut has_impl = false;
614+
tcx.for_each_impl(trait_def_id, |impl_def_id| {
615+
if has_impl {
616+
return;
617+
}
618+
619+
let self_ty = tcx
620+
.impl_trait_ref(impl_def_id)
621+
.expect("impl must have trait ref")
622+
.instantiate_identity()
623+
.self_ty();
624+
if self_ty.is_known_rigid() {
625+
return;
626+
}
627+
628+
let sized_trait = tcx.require_lang_item(LangItem::Sized, None);
629+
if tcx
630+
.param_env(impl_def_id)
631+
.caller_bounds()
632+
.iter()
633+
.filter_map(|clause| clause.as_trait_clause())
634+
.any(|bound| bound.def_id() == sized_trait && bound.self_ty().skip_binder() == self_ty)
635+
{
636+
return;
637+
}
638+
639+
if let ty::Alias(ty::Projection, alias_ty) = self_ty.kind()
640+
&& tcx
641+
.item_super_predicates(alias_ty.def_id)
642+
.iter_identity()
643+
.filter_map(|clause| clause.as_trait_clause())
644+
.any(|bound| bound.def_id() == sized_trait)
645+
{
646+
return;
647+
}
648+
649+
has_impl = true;
650+
});
651+
652+
has_impl
653+
}

compiler/rustc_type_ir/src/interner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ pub trait Interner:
273273
/// Returns `true` if this is an `unsafe trait`.
274274
fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool;
275275

276+
fn trait_has_impl_which_may_shadow_dyn(self, trait_def_id: Self::DefId) -> bool;
277+
276278
fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool;
277279

278280
fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed;

compiler/rustc_type_ir/src/predicate.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,10 @@ impl<I: Interner> ty::Binder<I, ExistentialProjection<I>> {
433433
pub fn item_def_id(&self) -> I::DefId {
434434
self.skip_binder().def_id
435435
}
436+
437+
pub fn trait_def_id(self, interner: I) -> I::DefId {
438+
interner.parent(self.skip_binder().def_id)
439+
}
436440
}
437441

438442
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/indirect-impl-for-trait-obj-coherence.rs:23:41
3+
|
4+
LL | fn transmute<T, U>(x: T) -> U {
5+
| - - expected type parameter
6+
| |
7+
| found type parameter
8+
LL | foo::<dyn Object<U, Output = T>, U>(x)
9+
| ----------------------------------- ^ expected type parameter `U`, found type parameter `T`
10+
| |
11+
| arguments to this function are incorrect
12+
|
13+
= note: expected type parameter `U`
14+
found type parameter `T`
15+
= note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
16+
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
17+
note: function defined here
18+
--> $DIR/indirect-impl-for-trait-obj-coherence.rs:17:4
19+
|
20+
LL | fn foo<T: ?Sized, U>(x: <T as Object<U>>::Output) -> U {
21+
| ^^^ ---------------------------
22+
23+
error: aborting due to 1 previous error
24+
25+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)