Skip to content

Commit 1b58a7f

Browse files
Item bounds can reference self projections and still be object safe
1 parent 50297bb commit 1b58a7f

File tree

2 files changed

+90
-43
lines changed

2 files changed

+90
-43
lines changed

compiler/rustc_trait_selection/src/traits/object_safety.rs

+79-43
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ use super::elaborate;
1212

1313
use crate::infer::TyCtxtInferExt;
1414
use crate::traits::query::evaluate_obligation::InferCtxtExt;
15-
use crate::traits::{self, Obligation, ObligationCause};
15+
use crate::traits::{Obligation, ObligationCause};
1616
use rustc_errors::FatalError;
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_span::symbol::Symbol;
2727
use rustc_span::Span;
@@ -195,7 +195,13 @@ fn predicates_reference_self(
195195
.predicates
196196
.iter()
197197
.map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, &trait_ref), sp))
198-
.filter_map(|predicate| predicate_references_self(tcx, predicate))
198+
.filter_map(|(clause, sp)| {
199+
// Super predicates cannot allow self projections, since they're
200+
// impossible to make into existential bounds without eager resolution
201+
// or something.
202+
// e.g. `trait A: B<Item = Self::Assoc>`.
203+
predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::No)
204+
})
199205
.collect()
200206
}
201207

@@ -204,20 +210,25 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
204210
.in_definition_order()
205211
.filter(|item| item.kind == ty::AssocKind::Type)
206212
.flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied())
207-
.filter_map(|c| predicate_references_self(tcx, c))
213+
.filter_map(|(clause, sp)| {
214+
// Item bounds *can* have self projections, since they never get
215+
// their self type erased.
216+
predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::Yes)
217+
})
208218
.collect()
209219
}
210220

211221
fn predicate_references_self<'tcx>(
212222
tcx: TyCtxt<'tcx>,
213-
(predicate, sp): (ty::Clause<'tcx>, Span),
223+
trait_def_id: DefId,
224+
predicate: ty::Clause<'tcx>,
225+
sp: Span,
226+
allow_self_projections: AllowSelfProjections,
214227
) -> Option<Span> {
215-
let self_ty = tcx.types.self_param;
216-
let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into());
217228
match predicate.kind().skip_binder() {
218229
ty::ClauseKind::Trait(ref data) => {
219230
// In the case of a trait predicate, we can skip the "self" type.
220-
data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp)
231+
data.trait_ref.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
221232
}
222233
ty::ClauseKind::Projection(ref data) => {
223234
// And similarly for projections. This should be redundant with
@@ -235,9 +246,9 @@ fn predicate_references_self<'tcx>(
235246
//
236247
// This is ALT2 in issue #56288, see that for discussion of the
237248
// possible alternatives.
238-
data.projection_term.args[1..].iter().any(has_self_ty).then_some(sp)
249+
data.projection_term.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
239250
}
240-
ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
251+
ty::ClauseKind::ConstArgHasType(_ct, ty) => contains_illegal_self_type_reference(tcx, trait_def_id, ty, allow_self_projections).then_some(sp),
241252

242253
ty::ClauseKind::WellFormed(..)
243254
| ty::ClauseKind::TypeOutlives(..)
@@ -383,7 +394,12 @@ fn virtual_call_violations_for_method<'tcx>(
383394
let mut errors = Vec::new();
384395

385396
for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) {
386-
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) {
397+
if contains_illegal_self_type_reference(
398+
tcx,
399+
trait_def_id,
400+
sig.rebind(input_ty),
401+
AllowSelfProjections::Yes,
402+
) {
387403
let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
388404
kind: hir::TraitItemKind::Fn(sig, _),
389405
..
@@ -396,7 +412,12 @@ fn virtual_call_violations_for_method<'tcx>(
396412
errors.push(MethodViolationCode::ReferencesSelfInput(span));
397413
}
398414
}
399-
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
415+
if contains_illegal_self_type_reference(
416+
tcx,
417+
trait_def_id,
418+
sig.output(),
419+
AllowSelfProjections::Yes,
420+
) {
400421
errors.push(MethodViolationCode::ReferencesSelfOutput);
401422
}
402423
if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
@@ -482,7 +503,7 @@ fn virtual_call_violations_for_method<'tcx>(
482503
return false;
483504
}
484505

485-
contains_illegal_self_type_reference(tcx, trait_def_id, pred)
506+
contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes)
486507
}) {
487508
errors.push(MethodViolationCode::WhereClauseReferencesSelf);
488509
}
@@ -711,10 +732,17 @@ fn receiver_is_dispatchable<'tcx>(
711732
infcx.predicate_must_hold_modulo_regions(&obligation)
712733
}
713734

735+
#[derive(Copy, Clone)]
736+
enum AllowSelfProjections {
737+
Yes,
738+
No,
739+
}
740+
714741
fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
715742
tcx: TyCtxt<'tcx>,
716743
trait_def_id: DefId,
717744
value: T,
745+
allow_self_projections: AllowSelfProjections,
718746
) -> bool {
719747
// This is somewhat subtle. In general, we want to forbid
720748
// references to `Self` in the argument and return types,
@@ -759,6 +787,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
759787
tcx: TyCtxt<'tcx>,
760788
trait_def_id: DefId,
761789
supertraits: Option<Vec<DefId>>,
790+
allow_self_projections: AllowSelfProjections,
762791
}
763792

764793
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
@@ -780,38 +809,40 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
780809
ControlFlow::Continue(())
781810
}
782811
ty::Alias(ty::Projection, ref data) => {
783-
// This is a projected type `<Foo as SomeTrait>::X`.
784-
785-
// Compute supertraits of current trait lazily.
786-
if self.supertraits.is_none() {
787-
let trait_ref =
788-
ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id));
789-
self.supertraits = Some(
790-
traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(),
791-
);
792-
}
812+
match self.allow_self_projections {
813+
AllowSelfProjections::Yes => {
814+
// This is a projected type `<Foo as SomeTrait>::X`.
815+
816+
// Compute supertraits of current trait lazily.
817+
if self.supertraits.is_none() {
818+
self.supertraits =
819+
Some(self.tcx.supertrait_def_ids(self.trait_def_id).collect());
820+
}
793821

794-
// Determine whether the trait reference `Foo as
795-
// SomeTrait` is in fact a supertrait of the
796-
// current trait. In that case, this type is
797-
// legal, because the type `X` will be specified
798-
// in the object type. Note that we can just use
799-
// direct equality here because all of these types
800-
// are part of the formal parameter listing, and
801-
// hence there should be no inference variables.
802-
let is_supertrait_of_current_trait = self
803-
.supertraits
804-
.as_ref()
805-
.unwrap()
806-
.contains(&data.trait_ref(self.tcx).def_id);
807-
808-
if is_supertrait_of_current_trait {
809-
ControlFlow::Continue(()) // do not walk contained types, do not report error, do collect $200
810-
} else {
811-
t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
822+
// Determine whether the trait reference `Foo as
823+
// SomeTrait` is in fact a supertrait of the
824+
// current trait. In that case, this type is
825+
// legal, because the type `X` will be specified
826+
// in the object type. Note that we can just use
827+
// direct equality here because all of these types
828+
// are part of the formal parameter listing, and
829+
// hence there should be no inference variables.
830+
let is_supertrait_of_current_trait = self
831+
.supertraits
832+
.as_ref()
833+
.unwrap()
834+
.contains(&data.trait_ref(self.tcx).def_id);
835+
836+
if is_supertrait_of_current_trait {
837+
ControlFlow::Continue(())
838+
} else {
839+
t.super_visit_with(self)
840+
}
841+
}
842+
AllowSelfProjections::No => t.super_visit_with(self),
812843
}
813844
}
814-
_ => t.super_visit_with(self), // walk contained types, if any
845+
_ => t.super_visit_with(self),
815846
}
816847
}
817848

@@ -823,7 +854,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
823854
}
824855

825856
value
826-
.visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None })
857+
.visit_with(&mut IllegalSelfTypeVisitor {
858+
tcx,
859+
trait_def_id,
860+
supertraits: None,
861+
allow_self_projections,
862+
})
827863
.is_break()
828864
}
829865

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)