Traits that reference `Self` in the supertrait list are not object-safe. Fixes #22040.

This commit is contained in:
Niko Matsakis 2015-02-17 10:57:15 -05:00
parent 02e1d5ec06
commit ff388c1277
5 changed files with 170 additions and 7 deletions

View File

@ -20,7 +20,7 @@
use super::supertraits;
use super::elaborate_predicates;
use middle::subst::{self, SelfSpace};
use middle::subst::{self, SelfSpace, TypeSpace};
use middle::traits;
use middle::ty::{self, Ty};
use std::rc::Rc;
@ -31,6 +31,10 @@ pub enum ObjectSafetyViolation<'tcx> {
/// Self : Sized declared on the trait
SizedSelf,
/// Supertrait reference references `Self` an in illegal location
/// (e.g. `trait Foo : Bar<Self>`)
SupertraitSelf,
/// Method has something illegal
Method(Rc<ty::Method<'tcx>>, MethodViolationCode),
}
@ -110,6 +114,9 @@ fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
if trait_has_sized_self(tcx, trait_def_id) {
violations.push(ObjectSafetyViolation::SizedSelf);
}
if supertraits_reference_self(tcx, trait_def_id) {
violations.push(ObjectSafetyViolation::SupertraitSelf);
}
debug!("object_safety_violations_for_trait(trait_def_id={}) = {}",
trait_def_id.repr(tcx),
@ -118,6 +125,34 @@ fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
violations
}
fn supertraits_reference_self<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId)
-> bool
{
let trait_def = ty::lookup_trait_def(tcx, trait_def_id);
let trait_ref = trait_def.trait_ref.clone();
let predicates = ty::predicates_for_trait_ref(tcx, &ty::Binder(trait_ref));
predicates
.into_iter()
.any(|predicate| {
match predicate {
ty::Predicate::Trait(ref data) => {
// In the case of a trait predicate, we can skip the "self" type.
data.0.trait_ref.substs.types.get_slice(TypeSpace)
.iter()
.cloned()
.any(is_self)
}
ty::Predicate::Projection(..) |
ty::Predicate::TypeOutlives(..) |
ty::Predicate::RegionOutlives(..) |
ty::Predicate::Equate(..) => {
false
}
}
})
}
fn trait_has_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId)
-> bool
@ -138,11 +173,7 @@ fn trait_has_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
.any(|predicate| {
match predicate {
ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => {
let self_ty = trait_pred.0.self_ty();
match self_ty.sty {
ty::ty_param(ref data) => data.space == subst::SelfSpace,
_ => false,
}
is_self(trait_pred.0.self_ty())
}
ty::Predicate::Projection(..) |
ty::Predicate::Trait(..) |
@ -295,8 +326,17 @@ impl<'tcx> Repr<'tcx> for ObjectSafetyViolation<'tcx> {
match *self {
ObjectSafetyViolation::SizedSelf =>
format!("SizedSelf"),
ObjectSafetyViolation::SupertraitSelf =>
format!("SupertraitSelf"),
ObjectSafetyViolation::Method(ref m, code) =>
format!("Method({},{:?})", m.repr(tcx), code),
}
}
}
fn is_self<'tcx>(ty: Ty<'tcx>) -> bool {
match ty.sty {
ty::ty_param(ref data) => data.space == subst::SelfSpace,
_ => false,
}
}

View File

@ -78,7 +78,7 @@ use std::marker;
use std::mem;
use std::ops;
use std::rc::Rc;
use std::vec::CowVec;
use std::vec::{CowVec, IntoIter};
use collections::enum_set::{EnumSet, CLike};
use std::collections::{HashMap, HashSet};
use syntax::abi;
@ -2034,6 +2034,40 @@ impl<'tcx> AsPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
}
impl<'tcx> Predicate<'tcx> {
/// Iterates over the types in this predicate. Note that in all
/// cases this is skipping over a binder, so late-bound regions
/// with depth 0 are bound by the predicate.
pub fn walk_tys(&self) -> IntoIter<Ty<'tcx>> {
let vec: Vec<_> = match *self {
ty::Predicate::Trait(ref data) => {
data.0.trait_ref.substs.types.as_slice().to_vec()
}
ty::Predicate::Equate(ty::Binder(ref data)) => {
vec![data.0, data.1]
}
ty::Predicate::TypeOutlives(ty::Binder(ref data)) => {
vec![data.0]
}
ty::Predicate::RegionOutlives(..) => {
vec![]
}
ty::Predicate::Projection(ref data) => {
let trait_inputs = data.0.projection_ty.trait_ref.substs.types.as_slice();
trait_inputs.iter()
.cloned()
.chain(Some(data.0.ty).into_iter())
.collect()
}
};
// The only reason to collect into a vector here is that I was
// too lazy to make the full (somewhat complicated) iterator
// type that would be needed here. But I wanted this fn to
// return an iterator conceptually, rather than a `Vec`, so as
// to be closer to `Ty::walk`.
vec.into_iter()
}
pub fn has_escaping_regions(&self) -> bool {
match *self {
Predicate::Trait(ref trait_ref) => trait_ref.has_escaping_regions(),

View File

@ -126,6 +126,13 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
"the trait cannot require that `Self : Sized`");
}
ObjectSafetyViolation::SupertraitSelf => {
tcx.sess.span_note(
span,
"the trait cannot use `Self` as a type parameter \
in the supertrait listing");
}
ObjectSafetyViolation::Method(method, MethodViolationCode::ByValueSelf) => {
tcx.sess.span_note(
span,

View File

@ -0,0 +1,50 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Regression test for #22040.
use std::fmt::Debug;
trait Expr: Debug + PartialEq {
fn print_element_count(&self);
}
//#[derive(PartialEq)]
#[derive(Debug)]
struct SExpr<'x> {
elements: Vec<Box<Expr+ 'x>>,
}
impl<'x> PartialEq for SExpr<'x> {
fn eq(&self, other:&SExpr<'x>) -> bool {
println!("L1: {} L2: {}", self.elements.len(), other.elements.len());
let result = self.elements.len() == other.elements.len();
println!("Got compare {}", result);
return result;
}
}
impl <'x> SExpr<'x> {
fn new() -> SExpr<'x> { return SExpr{elements: Vec::new(),}; }
}
impl <'x> Expr for SExpr<'x> {
fn print_element_count(&self) {
println!("element count: {}", self.elements.len());
}
}
fn main() {
let a: Box<Expr> = Box::new(SExpr::new()); //~ ERROR trait `Expr` is not object-safe
let b: Box<Expr> = Box::new(SExpr::new()); //~ ERROR trait `Expr` is not object-safe
assert_eq!(a , b);
}

View File

@ -0,0 +1,32 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Check that we correctly prevent users from making trait objects
// form traits that make use of `Self` in an argument or return position.
trait Bar<T> {
fn bar(&self, x: &T);
}
trait Baz : Bar<Self> {
}
fn make_bar<T:Bar<u32>>(t: &T) -> &Bar<u32> {
t
}
fn make_baz<T:Baz>(t: &T) -> &Baz {
t
//~^ ERROR `Baz` is not object-safe
//~| NOTE the trait cannot use `Self` as a type parameter in the supertrait listing
}
fn main() {
}