mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Item bounds can reference self projections and still be object safe
This commit is contained in:
parent
d3dd34a1d4
commit
8edf9b8084
@ -12,16 +12,16 @@ use super::elaborate;
|
|||||||
|
|
||||||
use crate::infer::TyCtxtInferExt;
|
use crate::infer::TyCtxtInferExt;
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
use crate::traits::{self, Obligation, ObligationCause};
|
use crate::traits::{Obligation, ObligationCause};
|
||||||
use rustc_errors::FatalError;
|
use rustc_errors::FatalError;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
|
use rustc_middle::ty::GenericArgs;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable,
|
self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable,
|
||||||
TypeVisitable, TypeVisitor,
|
TypeVisitable, TypeVisitor,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{GenericArg, GenericArgs};
|
|
||||||
use rustc_middle::ty::{TypeVisitableExt, Upcast};
|
use rustc_middle::ty::{TypeVisitableExt, Upcast};
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
@ -195,7 +195,13 @@ fn predicates_reference_self(
|
|||||||
.predicates
|
.predicates
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp))
|
.map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp))
|
||||||
.filter_map(|predicate| predicate_references_self(tcx, predicate))
|
.filter_map(|(clause, sp)| {
|
||||||
|
// Super predicates cannot allow self projections, since they're
|
||||||
|
// impossible to make into existential bounds without eager resolution
|
||||||
|
// or something.
|
||||||
|
// e.g. `trait A: B<Item = Self::Assoc>`.
|
||||||
|
predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::No)
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,20 +210,25 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
|
|||||||
.in_definition_order()
|
.in_definition_order()
|
||||||
.filter(|item| item.kind == ty::AssocKind::Type)
|
.filter(|item| item.kind == ty::AssocKind::Type)
|
||||||
.flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied())
|
.flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied())
|
||||||
.filter_map(|c| predicate_references_self(tcx, c))
|
.filter_map(|(clause, sp)| {
|
||||||
|
// Item bounds *can* have self projections, since they never get
|
||||||
|
// their self type erased.
|
||||||
|
predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::Yes)
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn predicate_references_self<'tcx>(
|
fn predicate_references_self<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
(predicate, sp): (ty::Clause<'tcx>, Span),
|
trait_def_id: DefId,
|
||||||
|
predicate: ty::Clause<'tcx>,
|
||||||
|
sp: Span,
|
||||||
|
allow_self_projections: AllowSelfProjections,
|
||||||
) -> Option<Span> {
|
) -> Option<Span> {
|
||||||
let self_ty = tcx.types.self_param;
|
|
||||||
let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into());
|
|
||||||
match predicate.kind().skip_binder() {
|
match predicate.kind().skip_binder() {
|
||||||
ty::ClauseKind::Trait(ref data) => {
|
ty::ClauseKind::Trait(ref data) => {
|
||||||
// In the case of a trait predicate, we can skip the "self" type.
|
// In the case of a trait predicate, we can skip the "self" type.
|
||||||
data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp)
|
data.trait_ref.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
|
||||||
}
|
}
|
||||||
ty::ClauseKind::Projection(ref data) => {
|
ty::ClauseKind::Projection(ref data) => {
|
||||||
// And similarly for projections. This should be redundant with
|
// And similarly for projections. This should be redundant with
|
||||||
@ -235,9 +246,9 @@ fn predicate_references_self<'tcx>(
|
|||||||
//
|
//
|
||||||
// This is ALT2 in issue #56288, see that for discussion of the
|
// This is ALT2 in issue #56288, see that for discussion of the
|
||||||
// possible alternatives.
|
// possible alternatives.
|
||||||
data.projection_term.args[1..].iter().any(has_self_ty).then_some(sp)
|
data.projection_term.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
|
||||||
}
|
}
|
||||||
ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
|
ty::ClauseKind::ConstArgHasType(_ct, ty) => contains_illegal_self_type_reference(tcx, trait_def_id, ty, allow_self_projections).then_some(sp),
|
||||||
|
|
||||||
ty::ClauseKind::WellFormed(..)
|
ty::ClauseKind::WellFormed(..)
|
||||||
| ty::ClauseKind::TypeOutlives(..)
|
| ty::ClauseKind::TypeOutlives(..)
|
||||||
@ -383,7 +394,12 @@ fn virtual_call_violations_for_method<'tcx>(
|
|||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) {
|
for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) {
|
||||||
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) {
|
if contains_illegal_self_type_reference(
|
||||||
|
tcx,
|
||||||
|
trait_def_id,
|
||||||
|
sig.rebind(input_ty),
|
||||||
|
AllowSelfProjections::Yes,
|
||||||
|
) {
|
||||||
let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
|
let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
|
||||||
kind: hir::TraitItemKind::Fn(sig, _),
|
kind: hir::TraitItemKind::Fn(sig, _),
|
||||||
..
|
..
|
||||||
@ -396,7 +412,12 @@ fn virtual_call_violations_for_method<'tcx>(
|
|||||||
errors.push(MethodViolationCode::ReferencesSelfInput(span));
|
errors.push(MethodViolationCode::ReferencesSelfInput(span));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
|
if contains_illegal_self_type_reference(
|
||||||
|
tcx,
|
||||||
|
trait_def_id,
|
||||||
|
sig.output(),
|
||||||
|
AllowSelfProjections::Yes,
|
||||||
|
) {
|
||||||
errors.push(MethodViolationCode::ReferencesSelfOutput);
|
errors.push(MethodViolationCode::ReferencesSelfOutput);
|
||||||
}
|
}
|
||||||
if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
|
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>(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
contains_illegal_self_type_reference(tcx, trait_def_id, pred)
|
contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes)
|
||||||
}) {
|
}) {
|
||||||
errors.push(MethodViolationCode::WhereClauseReferencesSelf);
|
errors.push(MethodViolationCode::WhereClauseReferencesSelf);
|
||||||
}
|
}
|
||||||
@ -711,10 +732,17 @@ fn receiver_is_dispatchable<'tcx>(
|
|||||||
infcx.predicate_must_hold_modulo_regions(&obligation)
|
infcx.predicate_must_hold_modulo_regions(&obligation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum AllowSelfProjections {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
|
fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_def_id: DefId,
|
trait_def_id: DefId,
|
||||||
value: T,
|
value: T,
|
||||||
|
allow_self_projections: AllowSelfProjections,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// This is somewhat subtle. In general, we want to forbid
|
// This is somewhat subtle. In general, we want to forbid
|
||||||
// references to `Self` in the argument and return types,
|
// references to `Self` in the argument and return types,
|
||||||
@ -759,6 +787,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
|
|||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_def_id: DefId,
|
trait_def_id: DefId,
|
||||||
supertraits: Option<Vec<DefId>>,
|
supertraits: Option<Vec<DefId>>,
|
||||||
|
allow_self_projections: AllowSelfProjections,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
|
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
|
||||||
@ -780,39 +809,41 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
|
|||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
ty::Alias(ty::Projection, ref data) => {
|
ty::Alias(ty::Projection, ref data) => {
|
||||||
// This is a projected type `<Foo as SomeTrait>::X`.
|
match self.allow_self_projections {
|
||||||
|
AllowSelfProjections::Yes => {
|
||||||
|
// This is a projected type `<Foo as SomeTrait>::X`.
|
||||||
|
|
||||||
// Compute supertraits of current trait lazily.
|
// Compute supertraits of current trait lazily.
|
||||||
if self.supertraits.is_none() {
|
if self.supertraits.is_none() {
|
||||||
let trait_ref =
|
self.supertraits =
|
||||||
ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id));
|
Some(self.tcx.supertrait_def_ids(self.trait_def_id).collect());
|
||||||
self.supertraits = Some(
|
}
|
||||||
traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine whether the trait reference `Foo as
|
// Determine whether the trait reference `Foo as
|
||||||
// SomeTrait` is in fact a supertrait of the
|
// SomeTrait` is in fact a supertrait of the
|
||||||
// current trait. In that case, this type is
|
// current trait. In that case, this type is
|
||||||
// legal, because the type `X` will be specified
|
// legal, because the type `X` will be specified
|
||||||
// in the object type. Note that we can just use
|
// in the object type. Note that we can just use
|
||||||
// direct equality here because all of these types
|
// direct equality here because all of these types
|
||||||
// are part of the formal parameter listing, and
|
// are part of the formal parameter listing, and
|
||||||
// hence there should be no inference variables.
|
// hence there should be no inference variables.
|
||||||
let is_supertrait_of_current_trait = self
|
let is_supertrait_of_current_trait = self
|
||||||
.supertraits
|
.supertraits
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.contains(&data.trait_ref(self.tcx).def_id);
|
.contains(&data.trait_ref(self.tcx).def_id);
|
||||||
|
|
||||||
// only walk contained types if it's not a super trait
|
// only walk contained types if it's not a super trait
|
||||||
if is_supertrait_of_current_trait {
|
if is_supertrait_of_current_trait {
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
} else {
|
} else {
|
||||||
t.super_visit_with(self) // POSSIBLY reporting an error
|
t.super_visit_with(self) // POSSIBLY reporting an error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AllowSelfProjections::No => t.super_visit_with(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => t.super_visit_with(self), // walk contained types, if any
|
_ => t.super_visit_with(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -824,7 +855,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
value
|
value
|
||||||
.visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None })
|
.visit_with(&mut IllegalSelfTypeVisitor {
|
||||||
|
tcx,
|
||||||
|
trait_def_id,
|
||||||
|
supertraits: None,
|
||||||
|
allow_self_projections,
|
||||||
|
})
|
||||||
.is_break()
|
.is_break()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
tests/ui/object-safety/item-bounds-can-reference-self.rs
Normal file
11
tests/ui/object-safety/item-bounds-can-reference-self.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
pub trait Foo {
|
||||||
|
type X: PartialEq;
|
||||||
|
type Y: PartialEq<Self::Y>;
|
||||||
|
type Z: PartialEq<Self::Y>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uwu(x: &dyn Foo<X = i32, Y = i32, Z = i32>) {}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Reference in New Issue
Block a user