Skip to content

Commit 1dcf764

Browse files
Item bounds can reference self projections and still be object safe
1 parent 5065123 commit 1dcf764

File tree

2 files changed

+91
-42
lines changed

2 files changed

+91
-42
lines changed

compiler/rustc_trait_selection/src/traits/object_safety.rs

+80-42
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ use rustc_errors::{DelayDm, FatalError, MultiSpan};
1717
use rustc_hir as hir;
1818
use rustc_hir::def_id::DefId;
1919
use rustc_middle::query::Providers;
20+
use rustc_middle::ty::GenericArgs;
2021
use rustc_middle::ty::{
2122
self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable,
2223
TypeVisitable, TypeVisitor,
2324
};
24-
use rustc_middle::ty::{GenericArg, GenericArgs};
2525
use rustc_middle::ty::{TypeVisitableExt, Upcast};
2626
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
2727
use rustc_span::symbol::Symbol;
@@ -265,7 +265,13 @@ fn predicates_reference_self(
265265
.predicates
266266
.iter()
267267
.map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, &trait_ref), sp))
268-
.filter_map(|predicate| predicate_references_self(tcx, predicate))
268+
.filter_map(|(clause, sp)| {
269+
// Super predicates cannot allow self projections, since they're
270+
// impossible to make into existential bounds without eager resolution
271+
// or something.
272+
// e.g. `trait A: B<Item = Self::Assoc>`.
273+
predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::No)
274+
})
269275
.collect()
270276
}
271277

@@ -274,20 +280,25 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
274280
.in_definition_order()
275281
.filter(|item| item.kind == ty::AssocKind::Type)
276282
.flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied())
277-
.filter_map(|c| predicate_references_self(tcx, c))
283+
.filter_map(|(clause, sp)| {
284+
// Item bounds *can* have self projections, since they never get
285+
// their self type erased.
286+
predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::Yes)
287+
})
278288
.collect()
279289
}
280290

281291
fn predicate_references_self<'tcx>(
282292
tcx: TyCtxt<'tcx>,
283-
(predicate, sp): (ty::Clause<'tcx>, Span),
293+
trait_def_id: DefId,
294+
predicate: ty::Clause<'tcx>,
295+
sp: Span,
296+
allow_self_projections: AllowSelfProjections,
284297
) -> Option<Span> {
285-
let self_ty = tcx.types.self_param;
286-
let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into());
287298
match predicate.kind().skip_binder() {
288299
ty::ClauseKind::Trait(ref data) => {
289300
// In the case of a trait predicate, we can skip the "self" type.
290-
data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp)
301+
data.trait_ref.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
291302
}
292303
ty::ClauseKind::Projection(ref data) => {
293304
// And similarly for projections. This should be redundant with
@@ -305,9 +316,9 @@ fn predicate_references_self<'tcx>(
305316
//
306317
// This is ALT2 in issue #56288, see that for discussion of the
307318
// possible alternatives.
308-
data.projection_term.args[1..].iter().any(has_self_ty).then_some(sp)
319+
data.projection_term.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
309320
}
310-
ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
321+
ty::ClauseKind::ConstArgHasType(_ct, ty) => contains_illegal_self_type_reference(tcx, trait_def_id, ty, allow_self_projections).then_some(sp),
311322

312323
ty::ClauseKind::WellFormed(..)
313324
| ty::ClauseKind::TypeOutlives(..)
@@ -453,7 +464,12 @@ fn virtual_call_violations_for_method<'tcx>(
453464
let mut errors = Vec::new();
454465

455466
for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) {
456-
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) {
467+
if contains_illegal_self_type_reference(
468+
tcx,
469+
trait_def_id,
470+
sig.rebind(input_ty),
471+
AllowSelfProjections::Yes,
472+
) {
457473
let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
458474
kind: hir::TraitItemKind::Fn(sig, _),
459475
..
@@ -466,7 +482,12 @@ fn virtual_call_violations_for_method<'tcx>(
466482
errors.push(MethodViolationCode::ReferencesSelfInput(span));
467483
}
468484
}
469-
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
485+
if contains_illegal_self_type_reference(
486+
tcx,
487+
trait_def_id,
488+
sig.output(),
489+
AllowSelfProjections::Yes,
490+
) {
470491
errors.push(MethodViolationCode::ReferencesSelfOutput);
471492
}
472493
if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
@@ -603,7 +624,7 @@ fn virtual_call_violations_for_method<'tcx>(
603624
return false;
604625
}
605626

606-
contains_illegal_self_type_reference(tcx, trait_def_id, pred)
627+
contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes)
607628
}) {
608629
errors.push(MethodViolationCode::WhereClauseReferencesSelf);
609630
}
@@ -783,10 +804,17 @@ fn receiver_is_dispatchable<'tcx>(
783804
infcx.predicate_must_hold_modulo_regions(&obligation)
784805
}
785806

807+
#[derive(Copy, Clone)]
808+
enum AllowSelfProjections {
809+
Yes,
810+
No,
811+
}
812+
786813
fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
787814
tcx: TyCtxt<'tcx>,
788815
trait_def_id: DefId,
789816
value: T,
817+
allow_self_projections: AllowSelfProjections,
790818
) -> bool {
791819
// This is somewhat subtle. In general, we want to forbid
792820
// references to `Self` in the argument and return types,
@@ -831,6 +859,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
831859
tcx: TyCtxt<'tcx>,
832860
trait_def_id: DefId,
833861
supertraits: Option<Vec<DefId>>,
862+
allow_self_projections: AllowSelfProjections,
834863
}
835864

836865
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
@@ -852,38 +881,42 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
852881
ControlFlow::Continue(())
853882
}
854883
ty::Alias(ty::Projection, ref data) => {
855-
// This is a projected type `<Foo as SomeTrait>::X`.
856-
857-
// Compute supertraits of current trait lazily.
858-
if self.supertraits.is_none() {
859-
let trait_ref =
860-
ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id));
861-
self.supertraits = Some(
862-
traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(),
863-
);
864-
}
884+
match self.allow_self_projections {
885+
AllowSelfProjections::Yes => {
886+
// This is a projected type `<Foo as SomeTrait>::X`.
887+
888+
// Compute supertraits of current trait lazily.
889+
if self.supertraits.is_none() {
890+
self.supertraits = Some(
891+
traits::supertrait_def_ids(self.tcx, self.trait_def_id)
892+
.collect(),
893+
);
894+
}
865895

866-
// Determine whether the trait reference `Foo as
867-
// SomeTrait` is in fact a supertrait of the
868-
// current trait. In that case, this type is
869-
// legal, because the type `X` will be specified
870-
// in the object type. Note that we can just use
871-
// direct equality here because all of these types
872-
// are part of the formal parameter listing, and
873-
// hence there should be no inference variables.
874-
let is_supertrait_of_current_trait = self
875-
.supertraits
876-
.as_ref()
877-
.unwrap()
878-
.contains(&data.trait_ref(self.tcx).def_id);
879-
880-
if is_supertrait_of_current_trait {
881-
ControlFlow::Continue(()) // do not walk contained types, do not report error, do collect $200
882-
} else {
883-
t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
896+
// Determine whether the trait reference `Foo as
897+
// SomeTrait` is in fact a supertrait of the
898+
// current trait. In that case, this type is
899+
// legal, because the type `X` will be specified
900+
// in the object type. Note that we can just use
901+
// direct equality here because all of these types
902+
// are part of the formal parameter listing, and
903+
// hence there should be no inference variables.
904+
let is_supertrait_of_current_trait = self
905+
.supertraits
906+
.as_ref()
907+
.unwrap()
908+
.contains(&data.trait_ref(self.tcx).def_id);
909+
910+
if is_supertrait_of_current_trait {
911+
ControlFlow::Continue(())
912+
} else {
913+
t.super_visit_with(self)
914+
}
915+
}
916+
AllowSelfProjections::No => t.super_visit_with(self),
884917
}
885918
}
886-
_ => t.super_visit_with(self), // walk contained types, if any
919+
_ => t.super_visit_with(self),
887920
}
888921
}
889922

@@ -895,7 +928,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
895928
}
896929

897930
value
898-
.visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None })
931+
.visit_with(&mut IllegalSelfTypeVisitor {
932+
tcx,
933+
trait_def_id,
934+
supertraits: None,
935+
allow_self_projections,
936+
})
899937
.is_break()
900938
}
901939

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//@ check-pass
2+
3+
pub trait Foo {
4+
type X: PartialEq;
5+
type Y: PartialEq<Self::Y>;
6+
type Z: PartialEq<Self::Y>;
7+
}
8+
9+
fn uwu(x: &dyn Foo<X = i32, Y = i32, Z = i32>) {}
10+
11+
fn main() {}

0 commit comments

Comments
 (0)