Skip to content

Commit 1820863

Browse files
Disqualify built-in trait impl if it seems likely to be overlap in an unsound way with a blanket impl
1 parent 2d0ea79 commit 1820863

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
@@ -1370,6 +1370,14 @@ rustc_queries! {
13701370
desc { |tcx| "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) }
13711371
}
13721372

1373+
query trait_has_impl_which_may_shadow_dyn(trait_def_id: DefId) -> bool {
1374+
desc {
1375+
|tcx| "checking if trait `{}` has an impl which may overlap with \
1376+
the built-in impl for trait objects",
1377+
tcx.def_path_str(trait_def_id)
1378+
}
1379+
}
1380+
13731381
/// Gets the ParameterEnvironment for a given item; this environment
13741382
/// will be in "user-facing" mode, meaning that it is suitable for
13751383
/// 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
@@ -578,6 +578,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
578578
self.trait_def(trait_def_id).implement_via_object
579579
}
580580

581+
fn trait_has_impl_which_may_shadow_dyn(self, trait_def_id: DefId) -> bool {
582+
self.trait_has_impl_which_may_shadow_dyn(trait_def_id)
583+
}
584+
581585
fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
582586
self.is_impl_trait_in_trait(def_id)
583587
}

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
@@ -824,6 +824,7 @@ pub fn provide(providers: &mut Providers) {
824824
specialization_graph_of: specialize::specialization_graph_provider,
825825
specializes: specialize::specializes,
826826
specialization_enabled_in: specialize::specialization_enabled_in,
827+
trait_has_impl_which_may_shadow_dyn: specialize::trait_has_impl_which_may_shadow_dyn,
827828
instantiate_and_check_impossible_predicates,
828829
is_impossible_associated_item,
829830
..*providers

compiler/rustc_trait_selection/src/traits/project.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
842842
let env_predicates = data
843843
.projection_bounds()
844844
.filter(|bound| bound.item_def_id() == obligation.predicate.def_id)
845+
.filter(|bound| !tcx.trait_has_impl_which_may_shadow_dyn(bound.trait_def_id(tcx)))
845846
.map(|p| p.with_self_ty(tcx, object_ty).upcast(tcx));
846847

847848
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
@@ -855,7 +855,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
855855
"assemble_candidates_from_object_ty",
856856
);
857857

858-
if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object {
858+
let tcx = self.tcx();
859+
if !tcx.trait_def(obligation.predicate.def_id()).implement_via_object {
859860
return;
860861
}
861862

@@ -876,9 +877,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
876877

877878
if let Some(principal) = data.principal() {
878879
if !self.infcx.tcx.features().dyn_compatible_for_dispatch() {
879-
principal.with_self_ty(self.tcx(), self_ty)
880-
} else if self.tcx().is_dyn_compatible(principal.def_id()) {
881-
principal.with_self_ty(self.tcx(), self_ty)
880+
principal.with_self_ty(tcx, self_ty)
881+
} else if tcx.is_dyn_compatible(principal.def_id()) {
882+
principal.with_self_ty(tcx, self_ty)
882883
} else {
883884
return;
884885
}
@@ -902,7 +903,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
902903
// correct trait, but also the correct type parameters.
903904
// For example, we may be trying to upcast `Foo` to `Bar<i32>`,
904905
// but `Foo` is declared as `trait Foo: Bar<u32>`.
905-
let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref)
906+
let candidate_supertraits = util::supertraits(tcx, principal_trait_ref)
906907
.enumerate()
907908
.filter(|&(_, upcast_trait_ref)| {
908909
self.infcx.probe(|_| {
@@ -914,6 +915,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
914915
.is_ok()
915916
})
916917
})
918+
.filter(|(_, trait_ref)| {
919+
!tcx.trait_has_impl_which_may_shadow_dyn(trait_ref.def_id())
920+
})
917921
.map(|(idx, _)| ObjectCandidate(idx));
918922

919923
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::infer::DefineOpaqueTypes;
1920
use rustc_middle::bug;
@@ -480,3 +481,58 @@ fn report_conflicting_impls<'tcx>(
480481
}
481482
}
482483
}
484+
485+
pub(super) fn trait_has_impl_which_may_shadow_dyn<'tcx>(
486+
tcx: TyCtxt<'tcx>,
487+
trait_def_id: DefId,
488+
) -> bool {
489+
// We only care about trait objects which have associated types.
490+
if !tcx
491+
.associated_items(trait_def_id)
492+
.in_definition_order()
493+
.any(|item| item.kind == ty::AssocKind::Type)
494+
{
495+
return false;
496+
}
497+
498+
let mut has_impl = false;
499+
tcx.for_each_impl(trait_def_id, |impl_def_id| {
500+
if has_impl {
501+
return;
502+
}
503+
504+
let self_ty = tcx
505+
.impl_trait_ref(impl_def_id)
506+
.expect("impl must have trait ref")
507+
.instantiate_identity()
508+
.self_ty();
509+
if self_ty.is_known_rigid() {
510+
return;
511+
}
512+
513+
let sized_trait = tcx.require_lang_item(LangItem::Sized, None);
514+
if tcx
515+
.param_env(impl_def_id)
516+
.caller_bounds()
517+
.iter()
518+
.filter_map(|clause| clause.as_trait_clause())
519+
.any(|bound| bound.def_id() == sized_trait && bound.self_ty().skip_binder() == self_ty)
520+
{
521+
return;
522+
}
523+
524+
if let ty::Alias(ty::Projection, alias_ty) = self_ty.kind()
525+
&& tcx
526+
.item_super_predicates(alias_ty.def_id)
527+
.iter_identity()
528+
.filter_map(|clause| clause.as_trait_clause())
529+
.any(|bound| bound.def_id() == sized_trait)
530+
{
531+
return;
532+
}
533+
534+
has_impl = true;
535+
});
536+
537+
has_impl
538+
}

compiler/rustc_type_ir/src/interner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ pub trait Interner:
269269

270270
fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool;
271271

272+
fn trait_has_impl_which_may_shadow_dyn(self, trait_def_id: Self::DefId) -> bool;
273+
272274
fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool;
273275

274276
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)