Skip to content

Commit 960aeed

Browse files
authored
Rollup merge of rust-lang#122804 - compiler-errors:item-bounds-can-reference-self, r=BoxyUwU
Item bounds can reference self projections and still be object safe ### Background Currently, we have some interesting rules about where `Self` is allowed to be mentioned in objects. Specifically, we allow mentioning `Self` behind associated types (e.g. `fn foo(&self) -> Self::Assoc`) only if that `Self` type comes from the trait we're defining or its supertraits: ``` trait Foo { fn good() -> Self::Assoc; // GOOD :) fn bad() -> <Self as OtherTrait>::Assoc; // BAD! } ``` And more specifically, these `Self::Assoc` projections are *only* allowed to show up in: * (A1) Method signatures * (A2) Where clauses on traits, GATs and methods But `Self::Assoc` projections are **not** allowed to show up in: * (B1) Supertrait bounds (specifically: all *super-predicates*, which includes the projections that come from elaboration, and not just the traits themselves). * (B2) Item bounds of associated types The reason for (B1) is interesting: specifically, it arises from the fact that we currently eagerly elaborate all projection predicates into the object, so if we had the following code: ``` trait Sub<Assoc = Self::SuperAssoc> {} trait Super { type SuperAssoc; } ``` Then given `dyn Sub<SuperAssoc = i32>` we would need to have a type that is substituted into itself an infinite number of times[^1], like `dyn Sub<SuperAssoc = i32, Assoc = <dyn Sub<SuperAssoc = i32, Assoc = <dyn Sub<SuperAssoc = i32, Assoc = <... as Super>::SuperAssoc> as Super>::SuperAssoc> as Super>::SuperAssoc>`, i.e. the fixed-point of: `type T = dyn Sub<SuperAssoc = i32, Assoc = <T as Super>::SuperAssoc>`. Similarly for (B2), we restrict mentioning `Self::Assoc` in associated type item bounds, which is the cause for rust-lang#122798. However, there is **no reason** for us to do so, since item bounds never show up structurally in the `dyn Trait` object type. #### What? This PR relaxes the check for item bounds so that `Self` may be mentioned behind associated types in the same cases that they currently work for method signatures (A1) and where clauses (A2). #### Why? Fixes rust-lang#122798. Removes a subtle and confusing inconsistency for the code mentioned in that issue. This is sound because we only assemble alias bounds for rigid projections, and all projections coming from an object self type are not rigid, since all associated types should be specified by the type. This is also desirable because we can do this via supertraits already. In rust-lang#122789, it is noted that an item bound of `Eq` already works, just not `PartialEq` because of the default item bound. This is weird and should be fixed. #### Future work We could make the check for `Self` in super-predicates more sophisticated as well, only erroring if `Self` shows up in a projection super-predicate. [^1]: This could be fixed by some sort of structural replacement or eager normalization, but I don't think it's necessary currently.
2 parents 90d6255 + 1dcf764 commit 960aeed

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::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,42 @@ 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 = Some(
819+
traits::supertrait_def_ids(self.tcx, self.trait_def_id)
820+
.collect(),
821+
);
822+
}
793823

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
824+
// Determine whether the trait reference `Foo as
825+
// SomeTrait` is in fact a supertrait of the
826+
// current trait. In that case, this type is
827+
// legal, because the type `X` will be specified
828+
// in the object type. Note that we can just use
829+
// direct equality here because all of these types
830+
// are part of the formal parameter listing, and
831+
// hence there should be no inference variables.
832+
let is_supertrait_of_current_trait = self
833+
.supertraits
834+
.as_ref()
835+
.unwrap()
836+
.contains(&data.trait_ref(self.tcx).def_id);
837+
838+
if is_supertrait_of_current_trait {
839+
ControlFlow::Continue(())
840+
} else {
841+
t.super_visit_with(self)
842+
}
843+
}
844+
AllowSelfProjections::No => t.super_visit_with(self),
812845
}
813846
}
814-
_ => t.super_visit_with(self), // walk contained types, if any
847+
_ => t.super_visit_with(self),
815848
}
816849
}
817850

@@ -823,7 +856,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
823856
}
824857

825858
value
826-
.visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None })
859+
.visit_with(&mut IllegalSelfTypeVisitor {
860+
tcx,
861+
trait_def_id,
862+
supertraits: None,
863+
allow_self_projections,
864+
})
827865
.is_break()
828866
}
829867

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)