Traits that reference `Self` in the supertrait list are not object-safe. Fixes #22040.
This commit is contained in:
parent
02e1d5ec06
commit
ff388c1277
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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() {
|
||||
}
|
Loading…
Reference in New Issue