Check associated type bounds for object safety violations

This commit is contained in:
Matthew Jasper 2020-06-28 17:36:41 +01:00
parent 5b279c8016
commit 1b07991574
3 changed files with 88 additions and 39 deletions

View File

@ -160,6 +160,10 @@ fn object_safety_violations_for_trait(
if !spans.is_empty() { if !spans.is_empty() {
violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); 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( violations.extend(
tcx.associated_items(trait_def_id) tcx.associated_items(trait_def_id)
@ -239,13 +243,31 @@ fn predicates_reference_self(
} else { } else {
tcx.predicates_of(trait_def_id) 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
.predicates .predicates
.iter() .iter()
.map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), *sp))
.filter_map(|(predicate, &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() { match predicate.skip_binders() {
ty::PredicateAtom::Trait(ref data, _) => { ty::PredicateAtom::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.
@ -257,6 +279,9 @@ fn predicates_reference_self(
// matching `Trait` predicate with the same inputs, but we do // matching `Trait` predicate with the same inputs, but we do
// the check to be safe. // 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 // Note that we *do* allow projection *outputs* to contain
// `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`), // `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`),
// we just require the user to specify *both* outputs // we just require the user to specify *both* outputs
@ -280,8 +305,6 @@ fn predicates_reference_self(
| ty::PredicateAtom::ConstEquate(..) | ty::PredicateAtom::ConstEquate(..)
| ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None,
} }
})
.collect()
} }
fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {

View 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() {}

View 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`.