Skip to content

Commit ff388c1

Browse files
committed
Traits that reference Self in the supertrait list are not object-safe. Fixes #22040.
1 parent 02e1d5e commit ff388c1

File tree

5 files changed

+170
-7
lines changed

5 files changed

+170
-7
lines changed

src/librustc/middle/traits/object_safety.rs

+46-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
use super::supertraits;
2121
use super::elaborate_predicates;
2222

23-
use middle::subst::{self, SelfSpace};
23+
use middle::subst::{self, SelfSpace, TypeSpace};
2424
use middle::traits;
2525
use middle::ty::{self, Ty};
2626
use std::rc::Rc;
@@ -31,6 +31,10 @@ pub enum ObjectSafetyViolation<'tcx> {
3131
/// Self : Sized declared on the trait
3232
SizedSelf,
3333

34+
/// Supertrait reference references `Self` an in illegal location
35+
/// (e.g. `trait Foo : Bar<Self>`)
36+
SupertraitSelf,
37+
3438
/// Method has something illegal
3539
Method(Rc<ty::Method<'tcx>>, MethodViolationCode),
3640
}
@@ -110,6 +114,9 @@ fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
110114
if trait_has_sized_self(tcx, trait_def_id) {
111115
violations.push(ObjectSafetyViolation::SizedSelf);
112116
}
117+
if supertraits_reference_self(tcx, trait_def_id) {
118+
violations.push(ObjectSafetyViolation::SupertraitSelf);
119+
}
113120

114121
debug!("object_safety_violations_for_trait(trait_def_id={}) = {}",
115122
trait_def_id.repr(tcx),
@@ -118,6 +125,34 @@ fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
118125
violations
119126
}
120127

128+
fn supertraits_reference_self<'tcx>(tcx: &ty::ctxt<'tcx>,
129+
trait_def_id: ast::DefId)
130+
-> bool
131+
{
132+
let trait_def = ty::lookup_trait_def(tcx, trait_def_id);
133+
let trait_ref = trait_def.trait_ref.clone();
134+
let predicates = ty::predicates_for_trait_ref(tcx, &ty::Binder(trait_ref));
135+
predicates
136+
.into_iter()
137+
.any(|predicate| {
138+
match predicate {
139+
ty::Predicate::Trait(ref data) => {
140+
// In the case of a trait predicate, we can skip the "self" type.
141+
data.0.trait_ref.substs.types.get_slice(TypeSpace)
142+
.iter()
143+
.cloned()
144+
.any(is_self)
145+
}
146+
ty::Predicate::Projection(..) |
147+
ty::Predicate::TypeOutlives(..) |
148+
ty::Predicate::RegionOutlives(..) |
149+
ty::Predicate::Equate(..) => {
150+
false
151+
}
152+
}
153+
})
154+
}
155+
121156
fn trait_has_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
122157
trait_def_id: ast::DefId)
123158
-> bool
@@ -138,11 +173,7 @@ fn trait_has_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
138173
.any(|predicate| {
139174
match predicate {
140175
ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => {
141-
let self_ty = trait_pred.0.self_ty();
142-
match self_ty.sty {
143-
ty::ty_param(ref data) => data.space == subst::SelfSpace,
144-
_ => false,
145-
}
176+
is_self(trait_pred.0.self_ty())
146177
}
147178
ty::Predicate::Projection(..) |
148179
ty::Predicate::Trait(..) |
@@ -295,8 +326,17 @@ impl<'tcx> Repr<'tcx> for ObjectSafetyViolation<'tcx> {
295326
match *self {
296327
ObjectSafetyViolation::SizedSelf =>
297328
format!("SizedSelf"),
329+
ObjectSafetyViolation::SupertraitSelf =>
330+
format!("SupertraitSelf"),
298331
ObjectSafetyViolation::Method(ref m, code) =>
299332
format!("Method({},{:?})", m.repr(tcx), code),
300333
}
301334
}
302335
}
336+
337+
fn is_self<'tcx>(ty: Ty<'tcx>) -> bool {
338+
match ty.sty {
339+
ty::ty_param(ref data) => data.space == subst::SelfSpace,
340+
_ => false,
341+
}
342+
}

src/librustc/middle/ty.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ use std::marker;
7878
use std::mem;
7979
use std::ops;
8080
use std::rc::Rc;
81-
use std::vec::CowVec;
81+
use std::vec::{CowVec, IntoIter};
8282
use collections::enum_set::{EnumSet, CLike};
8383
use std::collections::{HashMap, HashSet};
8484
use syntax::abi;
@@ -2034,6 +2034,40 @@ impl<'tcx> AsPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
20342034
}
20352035

20362036
impl<'tcx> Predicate<'tcx> {
2037+
/// Iterates over the types in this predicate. Note that in all
2038+
/// cases this is skipping over a binder, so late-bound regions
2039+
/// with depth 0 are bound by the predicate.
2040+
pub fn walk_tys(&self) -> IntoIter<Ty<'tcx>> {
2041+
let vec: Vec<_> = match *self {
2042+
ty::Predicate::Trait(ref data) => {
2043+
data.0.trait_ref.substs.types.as_slice().to_vec()
2044+
}
2045+
ty::Predicate::Equate(ty::Binder(ref data)) => {
2046+
vec![data.0, data.1]
2047+
}
2048+
ty::Predicate::TypeOutlives(ty::Binder(ref data)) => {
2049+
vec![data.0]
2050+
}
2051+
ty::Predicate::RegionOutlives(..) => {
2052+
vec![]
2053+
}
2054+
ty::Predicate::Projection(ref data) => {
2055+
let trait_inputs = data.0.projection_ty.trait_ref.substs.types.as_slice();
2056+
trait_inputs.iter()
2057+
.cloned()
2058+
.chain(Some(data.0.ty).into_iter())
2059+
.collect()
2060+
}
2061+
};
2062+
2063+
// The only reason to collect into a vector here is that I was
2064+
// too lazy to make the full (somewhat complicated) iterator
2065+
// type that would be needed here. But I wanted this fn to
2066+
// return an iterator conceptually, rather than a `Vec`, so as
2067+
// to be closer to `Ty::walk`.
2068+
vec.into_iter()
2069+
}
2070+
20372071
pub fn has_escaping_regions(&self) -> bool {
20382072
match *self {
20392073
Predicate::Trait(ref trait_ref) => trait_ref.has_escaping_regions(),

src/librustc_typeck/check/vtable.rs

+7
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
126126
"the trait cannot require that `Self : Sized`");
127127
}
128128

129+
ObjectSafetyViolation::SupertraitSelf => {
130+
tcx.sess.span_note(
131+
span,
132+
"the trait cannot use `Self` as a type parameter \
133+
in the supertrait listing");
134+
}
135+
129136
ObjectSafetyViolation::Method(method, MethodViolationCode::ByValueSelf) => {
130137
tcx.sess.span_note(
131138
span,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Regression test for #22040.
12+
13+
use std::fmt::Debug;
14+
15+
trait Expr: Debug + PartialEq {
16+
fn print_element_count(&self);
17+
}
18+
19+
//#[derive(PartialEq)]
20+
#[derive(Debug)]
21+
struct SExpr<'x> {
22+
elements: Vec<Box<Expr+ 'x>>,
23+
}
24+
25+
impl<'x> PartialEq for SExpr<'x> {
26+
fn eq(&self, other:&SExpr<'x>) -> bool {
27+
println!("L1: {} L2: {}", self.elements.len(), other.elements.len());
28+
let result = self.elements.len() == other.elements.len();
29+
30+
println!("Got compare {}", result);
31+
return result;
32+
}
33+
}
34+
35+
impl <'x> SExpr<'x> {
36+
fn new() -> SExpr<'x> { return SExpr{elements: Vec::new(),}; }
37+
}
38+
39+
impl <'x> Expr for SExpr<'x> {
40+
fn print_element_count(&self) {
41+
println!("element count: {}", self.elements.len());
42+
}
43+
}
44+
45+
fn main() {
46+
let a: Box<Expr> = Box::new(SExpr::new()); //~ ERROR trait `Expr` is not object-safe
47+
let b: Box<Expr> = Box::new(SExpr::new()); //~ ERROR trait `Expr` is not object-safe
48+
49+
assert_eq!(a , b);
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that we correctly prevent users from making trait objects
12+
// form traits that make use of `Self` in an argument or return position.
13+
14+
trait Bar<T> {
15+
fn bar(&self, x: &T);
16+
}
17+
18+
trait Baz : Bar<Self> {
19+
}
20+
21+
fn make_bar<T:Bar<u32>>(t: &T) -> &Bar<u32> {
22+
t
23+
}
24+
25+
fn make_baz<T:Baz>(t: &T) -> &Baz {
26+
t
27+
//~^ ERROR `Baz` is not object-safe
28+
//~| NOTE the trait cannot use `Self` as a type parameter in the supertrait listing
29+
}
30+
31+
fn main() {
32+
}

0 commit comments

Comments
 (0)