mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Check associated type bounds for object safety violations
This commit is contained in:
parent
5b279c8016
commit
1b07991574
@ -160,6 +160,10 @@ fn object_safety_violations_for_trait(
|
||||
if !spans.is_empty() {
|
||||
violations.push(ObjectSafetyViolation::SupertraitSelf(spans));
|
||||
}
|
||||
let spans = bounds_reference_self(tcx, trait_def_id);
|
||||
if !spans.is_empty() {
|
||||
violations.push(ObjectSafetyViolation::SupertraitSelf(spans));
|
||||
}
|
||||
|
||||
violations.extend(
|
||||
tcx.associated_items(trait_def_id)
|
||||
@ -239,51 +243,70 @@ fn predicates_reference_self(
|
||||
} else {
|
||||
tcx.predicates_of(trait_def_id)
|
||||
};
|
||||
let self_ty = tcx.types.self_param;
|
||||
let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into());
|
||||
predicates
|
||||
.predicates
|
||||
.iter()
|
||||
.map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp))
|
||||
.filter_map(|(predicate, &sp)| {
|
||||
match predicate.skip_binders() {
|
||||
ty::PredicateAtom::Trait(ref data, _) => {
|
||||
// In the case of a trait predicate, we can skip the "self" type.
|
||||
if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None }
|
||||
}
|
||||
ty::PredicateAtom::Projection(ref data) => {
|
||||
// And similarly for projections. This should be redundant with
|
||||
// the previous check because any projection should have a
|
||||
// matching `Trait` predicate with the same inputs, but we do
|
||||
// the check to be safe.
|
||||
//
|
||||
// Note that we *do* allow projection *outputs* to contain
|
||||
// `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
|
||||
// we just require the user to specify *both* outputs
|
||||
// in the object type (i.e., `dyn Foo<Output=(), Result=()>`).
|
||||
//
|
||||
// This is ALT2 in issue #56288, see that for discussion of the
|
||||
// possible alternatives.
|
||||
if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) {
|
||||
Some(sp)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ty::PredicateAtom::WellFormed(..)
|
||||
| ty::PredicateAtom::ObjectSafe(..)
|
||||
| ty::PredicateAtom::TypeOutlives(..)
|
||||
| ty::PredicateAtom::RegionOutlives(..)
|
||||
| ty::PredicateAtom::ClosureKind(..)
|
||||
| ty::PredicateAtom::Subtype(..)
|
||||
| ty::PredicateAtom::ConstEvaluatable(..)
|
||||
| ty::PredicateAtom::ConstEquate(..)
|
||||
| ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
|
||||
}
|
||||
})
|
||||
.map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), *sp))
|
||||
.filter_map(|predicate| predicate_references_self(tcx, predicate))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> {
|
||||
let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
|
||||
tcx.associated_items(trait_def_id)
|
||||
.in_definition_order()
|
||||
.filter(|item| item.kind == ty::AssocKind::Type)
|
||||
.flat_map(|item| tcx.explicit_item_bounds(item.def_id))
|
||||
.map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), *sp))
|
||||
.filter_map(|predicate| predicate_references_self(tcx, predicate))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn predicate_references_self(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
(predicate, sp): (ty::Predicate<'tcx>, Span),
|
||||
) -> Option<Span> {
|
||||
let self_ty = tcx.types.self_param;
|
||||
let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into());
|
||||
match predicate.skip_binders() {
|
||||
ty::PredicateAtom::Trait(ref data, _) => {
|
||||
// In the case of a trait predicate, we can skip the "self" type.
|
||||
if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None }
|
||||
}
|
||||
ty::PredicateAtom::Projection(ref data) => {
|
||||
// And similarly for projections. This should be redundant with
|
||||
// the previous check because any projection should have a
|
||||
// matching `Trait` predicate with the same inputs, but we do
|
||||
// the check to be safe.
|
||||
//
|
||||
// It's also won't be redundant if we allow type-generic associated
|
||||
// types for trait objects.
|
||||
//
|
||||
// Note that we *do* allow projection *outputs* to contain
|
||||
// `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
|
||||
// we just require the user to specify *both* outputs
|
||||
// in the object type (i.e., `dyn Foo<Output=(), Result=()>`).
|
||||
//
|
||||
// This is ALT2 in issue #56288, see that for discussion of the
|
||||
// possible alternatives.
|
||||
if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) {
|
||||
Some(sp)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ty::PredicateAtom::WellFormed(..)
|
||||
| ty::PredicateAtom::ObjectSafe(..)
|
||||
| ty::PredicateAtom::TypeOutlives(..)
|
||||
| ty::PredicateAtom::RegionOutlives(..)
|
||||
| ty::PredicateAtom::ClosureKind(..)
|
||||
| ty::PredicateAtom::Subtype(..)
|
||||
| ty::PredicateAtom::ConstEvaluatable(..)
|
||||
| ty::PredicateAtom::ConstEquate(..)
|
||||
| ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
|
||||
generics_require_sized_self(tcx, trait_def_id)
|
||||
}
|
||||
|
12
src/test/ui/object-safety/object-safety-bounds.rs
Normal file
12
src/test/ui/object-safety/object-safety-bounds.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// Traits with bounds mentioning `Self` are not object safe
|
||||
|
||||
trait X {
|
||||
type U: PartialEq<Self>;
|
||||
}
|
||||
|
||||
fn f() -> Box<dyn X<U = u32>> {
|
||||
//~^ ERROR the trait `X` cannot be made into an object
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn main() {}
|
14
src/test/ui/object-safety/object-safety-bounds.stderr
Normal file
14
src/test/ui/object-safety/object-safety-bounds.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error[E0038]: the trait `X` cannot be made into an object
|
||||
--> $DIR/object-safety-bounds.rs:7:11
|
||||
|
|
||||
LL | trait X {
|
||||
| - this trait cannot be made into an object...
|
||||
LL | type U: PartialEq<Self>;
|
||||
| --------------- ...because it uses `Self` as a type parameter in this
|
||||
...
|
||||
LL | fn f() -> Box<dyn X<U = u32>> {
|
||||
| ^^^^^^^^^^^^^^^^^^^ the trait `X` cannot be made into an object
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0038`.
|
Loading…
Reference in New Issue
Block a user