Skip to content

Commit 3184f93

Browse files
Item bounds can reference self projections and still be object safe
1 parent e3df96c commit 3184f93

File tree

2 files changed

+91
-43
lines changed

2 files changed

+91
-43
lines changed

compiler/rustc_trait_selection/src/traits/object_safety.rs

+80-43
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ use rustc_hir as hir;
1818
use rustc_hir::def_id::DefId;
1919
use rustc_middle::query::Providers;
2020
use rustc_middle::ty::{
21-
self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
21+
self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
2222
};
23-
use rustc_middle::ty::{GenericArg, GenericArgs};
2423
use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
2524
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
2625
use rustc_span::symbol::Symbol;
@@ -264,7 +263,13 @@ fn predicates_reference_self(
264263
.predicates
265264
.iter()
266265
.map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, &trait_ref), sp))
267-
.filter_map(|predicate| predicate_references_self(tcx, predicate))
266+
.filter_map(|(clause, sp)| {
267+
// Super predicates cannot allow self projections, since they're
268+
// impossible to make into existential bounds without eager resolution
269+
// or something.
270+
// e.g. `trait A: B<Item = Self::Assoc>`.
271+
predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::No)
272+
})
268273
.collect()
269274
}
270275

@@ -273,20 +278,25 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
273278
.in_definition_order()
274279
.filter(|item| item.kind == ty::AssocKind::Type)
275280
.flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied())
276-
.filter_map(|c| predicate_references_self(tcx, c))
281+
.filter_map(|(clause, sp)| {
282+
// Item bounds *can* have self projections, since they never get
283+
// their self type erased.
284+
predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::Yes)
285+
})
277286
.collect()
278287
}
279288

280289
fn predicate_references_self<'tcx>(
281290
tcx: TyCtxt<'tcx>,
282-
(predicate, sp): (ty::Clause<'tcx>, Span),
291+
trait_def_id: DefId,
292+
predicate: ty::Clause<'tcx>,
293+
sp: Span,
294+
allow_self_projections: AllowSelfProjections,
283295
) -> Option<Span> {
284-
let self_ty = tcx.types.self_param;
285-
let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into());
286296
match predicate.kind().skip_binder() {
287297
ty::ClauseKind::Trait(ref data) => {
288298
// In the case of a trait predicate, we can skip the "self" type.
289-
data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp)
299+
data.trait_ref.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
290300
}
291301
ty::ClauseKind::Projection(ref data) => {
292302
// And similarly for projections. This should be redundant with
@@ -304,9 +314,9 @@ fn predicate_references_self<'tcx>(
304314
//
305315
// This is ALT2 in issue #56288, see that for discussion of the
306316
// possible alternatives.
307-
data.projection_ty.args[1..].iter().any(has_self_ty).then_some(sp)
317+
data.projection_ty.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
308318
}
309-
ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
319+
ty::ClauseKind::ConstArgHasType(_ct, ty) => contains_illegal_self_type_reference(tcx, trait_def_id, ty, allow_self_projections).then_some(sp),
310320

311321
ty::ClauseKind::WellFormed(..)
312322
| ty::ClauseKind::TypeOutlives(..)
@@ -452,7 +462,12 @@ fn virtual_call_violations_for_method<'tcx>(
452462
let mut errors = Vec::new();
453463

454464
for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) {
455-
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) {
465+
if contains_illegal_self_type_reference(
466+
tcx,
467+
trait_def_id,
468+
sig.rebind(input_ty),
469+
AllowSelfProjections::Yes,
470+
) {
456471
let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
457472
kind: hir::TraitItemKind::Fn(sig, _),
458473
..
@@ -465,7 +480,12 @@ fn virtual_call_violations_for_method<'tcx>(
465480
errors.push(MethodViolationCode::ReferencesSelfInput(span));
466481
}
467482
}
468-
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
483+
if contains_illegal_self_type_reference(
484+
tcx,
485+
trait_def_id,
486+
sig.output(),
487+
AllowSelfProjections::Yes,
488+
) {
469489
errors.push(MethodViolationCode::ReferencesSelfOutput);
470490
}
471491
if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
@@ -602,7 +622,7 @@ fn virtual_call_violations_for_method<'tcx>(
602622
return false;
603623
}
604624

605-
contains_illegal_self_type_reference(tcx, trait_def_id, pred)
625+
contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes)
606626
}) {
607627
errors.push(MethodViolationCode::WhereClauseReferencesSelf);
608628
}
@@ -783,10 +803,17 @@ fn receiver_is_dispatchable<'tcx>(
783803
infcx.predicate_must_hold_modulo_regions(&obligation)
784804
}
785805

806+
#[derive(Copy, Clone)]
807+
enum AllowSelfProjections {
808+
Yes,
809+
No,
810+
}
811+
786812
fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
787813
tcx: TyCtxt<'tcx>,
788814
trait_def_id: DefId,
789815
value: T,
816+
allow_self_projections: AllowSelfProjections,
790817
) -> bool {
791818
// This is somewhat subtle. In general, we want to forbid
792819
// references to `Self` in the argument and return types,
@@ -831,6 +858,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
831858
tcx: TyCtxt<'tcx>,
832859
trait_def_id: DefId,
833860
supertraits: Option<Vec<DefId>>,
861+
allow_self_projections: AllowSelfProjections,
834862
}
835863

836864
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
@@ -852,38 +880,42 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
852880
ControlFlow::Continue(())
853881
}
854882
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-
}
883+
match self.allow_self_projections {
884+
AllowSelfProjections::Yes => {
885+
// This is a projected type `<Foo as SomeTrait>::X`.
886+
887+
// Compute supertraits of current trait lazily.
888+
if self.supertraits.is_none() {
889+
self.supertraits = Some(
890+
traits::supertrait_def_ids(self.tcx, self.trait_def_id)
891+
.collect(),
892+
);
893+
}
865894

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
895+
// Determine whether the trait reference `Foo as
896+
// SomeTrait` is in fact a supertrait of the
897+
// current trait. In that case, this type is
898+
// legal, because the type `X` will be specified
899+
// in the object type. Note that we can just use
900+
// direct equality here because all of these types
901+
// are part of the formal parameter listing, and
902+
// hence there should be no inference variables.
903+
let is_supertrait_of_current_trait = self
904+
.supertraits
905+
.as_ref()
906+
.unwrap()
907+
.contains(&data.trait_ref(self.tcx).def_id);
908+
909+
if is_supertrait_of_current_trait {
910+
ControlFlow::Continue(())
911+
} else {
912+
t.super_visit_with(self)
913+
}
914+
}
915+
AllowSelfProjections::No => t.super_visit_with(self),
884916
}
885917
}
886-
_ => t.super_visit_with(self), // walk contained types, if any
918+
_ => t.super_visit_with(self),
887919
}
888920
}
889921

@@ -895,7 +927,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
895927
}
896928

897929
value
898-
.visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None })
930+
.visit_with(&mut IllegalSelfTypeVisitor {
931+
tcx,
932+
trait_def_id,
933+
supertraits: None,
934+
allow_self_projections,
935+
})
899936
.is_break()
900937
}
901938

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)