Skip to content

Commit 261364d

Browse files
committed
rollup merge of rust-lang#22452: nikomatsakis/issue-22040-18956-Self
The big change here is that we update the object-safety rules to prohibit references to `Self` in the supertrait listing. See rust-lang#22040 for the motivation. The other change is to handle the interaction of defaults that reference `Self` and object types (where `Self` is erased). We force users to give an explicit type in that scenario. r? @aturon
2 parents d8753a0 + ff388c1 commit 261364d

9 files changed

+251
-13
lines changed

src/librustc/middle/traits/object_safety.rs

Lines changed: 46 additions & 6 deletions
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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ use std::hash::{Hash, Writer, SipHasher, Hasher};
7676
use std::mem;
7777
use std::ops;
7878
use std::rc::Rc;
79-
use std::vec::CowVec;
79+
use std::vec::{CowVec, IntoIter};
8080
use collections::enum_set::{EnumSet, CLike};
8181
use std::collections::{HashMap, HashSet};
8282
use syntax::abi;
@@ -2004,6 +2004,40 @@ impl<'tcx> AsPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
20042004
}
20052005

20062006
impl<'tcx> Predicate<'tcx> {
2007+
/// Iterates over the types in this predicate. Note that in all
2008+
/// cases this is skipping over a binder, so late-bound regions
2009+
/// with depth 0 are bound by the predicate.
2010+
pub fn walk_tys(&self) -> IntoIter<Ty<'tcx>> {
2011+
let vec: Vec<_> = match *self {
2012+
ty::Predicate::Trait(ref data) => {
2013+
data.0.trait_ref.substs.types.as_slice().to_vec()
2014+
}
2015+
ty::Predicate::Equate(ty::Binder(ref data)) => {
2016+
vec![data.0, data.1]
2017+
}
2018+
ty::Predicate::TypeOutlives(ty::Binder(ref data)) => {
2019+
vec![data.0]
2020+
}
2021+
ty::Predicate::RegionOutlives(..) => {
2022+
vec![]
2023+
}
2024+
ty::Predicate::Projection(ref data) => {
2025+
let trait_inputs = data.0.projection_ty.trait_ref.substs.types.as_slice();
2026+
trait_inputs.iter()
2027+
.cloned()
2028+
.chain(Some(data.0.ty).into_iter())
2029+
.collect()
2030+
}
2031+
};
2032+
2033+
// The only reason to collect into a vector here is that I was
2034+
// too lazy to make the full (somewhat complicated) iterator
2035+
// type that would be needed here. But I wanted this fn to
2036+
// return an iterator conceptually, rather than a `Vec`, so as
2037+
// to be closer to `Ty::walk`.
2038+
vec.into_iter()
2039+
}
2040+
20072041
pub fn has_escaping_regions(&self) -> bool {
20082042
match *self {
20092043
Predicate::Trait(ref trait_ref) => trait_ref.has_escaping_regions(),

src/librustc/util/ppaux.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,13 +508,26 @@ pub fn parameterized<'tcx,GG>(cx: &ctxt<'tcx>,
508508
// avoid those ICEs.
509509
let generics = get_generics();
510510

511+
let has_self = substs.self_ty().is_some();
511512
let tps = substs.types.get_slice(subst::TypeSpace);
512513
let ty_params = generics.types.get_slice(subst::TypeSpace);
513514
let has_defaults = ty_params.last().map_or(false, |def| def.default.is_some());
514515
let num_defaults = if has_defaults {
515516
ty_params.iter().zip(tps.iter()).rev().take_while(|&(def, &actual)| {
516517
match def.default {
517-
Some(default) => default.subst(cx, substs) == actual,
518+
Some(default) => {
519+
if !has_self && ty::type_has_self(default) {
520+
// In an object type, there is no `Self`, and
521+
// thus if the default value references Self,
522+
// the user will be required to give an
523+
// explicit value. We can't even do the
524+
// substitution below to check without causing
525+
// an ICE. (#18956).
526+
false
527+
} else {
528+
default.subst(cx, substs) == actual
529+
}
530+
}
518531
None => false
519532
}
520533
}).count()

src/librustc_typeck/astconv.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -404,17 +404,30 @@ fn create_substs_for_ast_path<'tcx>(
404404

405405
let actual_supplied_ty_param_count = substs.types.len(TypeSpace);
406406
for param in &ty_param_defs[actual_supplied_ty_param_count..] {
407-
match param.default {
408-
Some(default) => {
407+
if let Some(default) = param.default {
408+
// If we are converting an object type, then the
409+
// `Self` parameter is unknown. However, some of the
410+
// other type parameters may reference `Self` in their
411+
// defaults. This will lead to an ICE if we are not
412+
// careful!
413+
if self_ty.is_none() && ty::type_has_self(default) {
414+
tcx.sess.span_err(
415+
span,
416+
&format!("the type parameter `{}` must be explicitly specified \
417+
in an object type because its default value `{}` references \
418+
the type `Self`",
419+
param.name.user_string(tcx),
420+
default.user_string(tcx)));
421+
substs.types.push(TypeSpace, tcx.types.err);
422+
} else {
409423
// This is a default type parameter.
410424
let default = default.subst_spanned(tcx,
411425
&substs,
412426
Some(span));
413427
substs.types.push(TypeSpace, default);
414428
}
415-
None => {
416-
tcx.sess.span_bug(span, "extra parameter without default");
417-
}
429+
} else {
430+
tcx.sess.span_bug(span, "extra parameter without default");
418431
}
419432
}
420433

src/librustc_typeck/check/vtable.rs

Lines changed: 7 additions & 0 deletions
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,
Lines changed: 50 additions & 0 deletions
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+
}
Lines changed: 32 additions & 0 deletions
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+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
// Test a default that references `Self` which is then used in an
12+
// object type. Issue #18956. In this case, the value is supplied by
13+
// the user, but pretty-printing the type during the error message
14+
// caused an ICE.
15+
16+
trait MyAdd<Rhs=Self> { fn add(&self, other: &Rhs) -> Self; }
17+
18+
impl MyAdd for i32 {
19+
fn add(&self, other: &i32) -> i32 { *self + *other }
20+
}
21+
22+
fn main() {
23+
let x = 5;
24+
let y = x as MyAdd<i32>;
25+
//~^ ERROR as `MyAdd<i32>`
26+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
// Test a default that references `Self` which is then used in an object type.
12+
// Issue #18956.
13+
14+
#![feature(default_type_params)]
15+
16+
trait Foo<T=Self> {
17+
fn method(&self);
18+
}
19+
20+
fn foo(x: &Foo) { }
21+
//~^ ERROR the type parameter `T` must be explicitly specified
22+
23+
fn main() { }

0 commit comments

Comments
 (0)