Skip to content

Commit

Permalink
Rollup merge of rust-lang#22301 - nikomatsakis:object-safe-sized-meth…
Browse files Browse the repository at this point in the history
…ods, r=huonw

 RFC 817 is not yet accepted, but I wanted to put this code up so people can see how it works. And to be ready lest it should be accepted.

cc rust-lang/rfcs#817
  • Loading branch information
Manishearth committed Feb 24, 2015
2 parents 8e88762 + fd9f7da commit 5676f60
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 66 deletions.
37 changes: 22 additions & 15 deletions src/librustc/middle/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ pub enum ObjectSafetyViolation<'tcx> {
/// Reasons a method might not be object-safe.
#[derive(Copy,Clone,Debug)]
pub enum MethodViolationCode {
/// e.g., `fn(self)`
ByValueSelf,

/// e.g., `fn foo()`
StaticMethod,

Expand Down Expand Up @@ -157,19 +154,25 @@ fn supertraits_reference_self<'tcx>(tcx: &ty::ctxt<'tcx>,
fn trait_has_sized_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_predicates = ty::lookup_predicates(tcx, trait_def_id);
generics_require_sized_self(tcx, &trait_def.generics, &trait_predicates)
}

fn generics_require_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
generics: &ty::Generics<'tcx>,
predicates: &ty::GenericPredicates<'tcx>)
-> bool
{
let sized_def_id = match tcx.lang_items.sized_trait() {
Some(def_id) => def_id,
None => { return false; /* No Sized trait, can't require it! */ }
};

// Search for a predicate like `Self : Sized` amongst the trait bounds.
let trait_def = ty::lookup_trait_def(tcx, trait_def_id);
let free_substs = ty::construct_free_substs(tcx, &trait_def.generics, ast::DUMMY_NODE_ID);

let trait_predicates = ty::lookup_predicates(tcx, trait_def_id);
let predicates = trait_predicates.instantiate(tcx, &free_substs).predicates.into_vec();

let free_substs = ty::construct_free_substs(tcx, generics, ast::DUMMY_NODE_ID);
let predicates = predicates.instantiate(tcx, &free_substs).predicates.into_vec();
elaborate_predicates(tcx, predicates)
.any(|predicate| {
match predicate {
Expand All @@ -192,17 +195,21 @@ fn object_safety_violations_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
method: &ty::Method<'tcx>)
-> Option<MethodViolationCode>
{
// The method's first parameter must be something that derefs to
// `&self`. For now, we only accept `&self` and `Box<Self>`.
match method.explicit_self {
ty::ByValueExplicitSelfCategory => {
return Some(MethodViolationCode::ByValueSelf);
}
// Any method that has a `Self : Sized` requisite is otherwise
// exempt from the regulations.
if generics_require_sized_self(tcx, &method.generics, &method.predicates) {
return None;
}

// The method's first parameter must be something that derefs (or
// autorefs) to `&self`. For now, we only accept `self`, `&self`
// and `Box<Self>`.
match method.explicit_self {
ty::StaticExplicitSelfCategory => {
return Some(MethodViolationCode::StaticMethod);
}

ty::ByValueExplicitSelfCategory |
ty::ByReferenceExplicitSelfCategory(..) |
ty::ByBoxExplicitSelfCategory => {
}
Expand Down
3 changes: 0 additions & 3 deletions src/librustc_trans/trans/meth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,9 +818,6 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
param_substs,
substs.clone()).val;

// currently, at least, by-value self is not object safe
assert!(m.explicit_self != ty::ByValueExplicitSelfCategory);

Some(fn_ref).into_iter()
}
}
Expand Down
23 changes: 3 additions & 20 deletions src/librustc_typeck/check/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,26 +404,9 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
all_substs.repr(self.tcx()));

// Instantiate the bounds on the method with the
// type/early-bound-regions substitutions performed. The only
// late-bound-regions that can appear in bounds are from the
// impl, and those were already instantiated above.
//
// FIXME(DST). Super hack. For a method on a trait object
// `Trait`, the generic signature requires that
// `Self:Trait`. Since, for an object, we bind `Self` to the
// type `Trait`, this leads to an obligation
// `Trait:Trait`. Until such time we DST is fully implemented,
// that obligation is not necessarily satisfied. (In the
// future, it would be.) But we know that the true `Self` DOES implement
// the trait. So we just delete this requirement. Hack hack hack.
let mut method_predicates = pick.method_ty.predicates.instantiate(self.tcx(), &all_substs);
match pick.kind {
probe::ObjectPick(..) => {
assert_eq!(method_predicates.predicates.get_slice(subst::SelfSpace).len(), 1);
method_predicates.predicates.pop(subst::SelfSpace);
}
_ => { }
}
// type/early-bound-regions substitutions performed. There can
// be no late-bound regions appearing here.
let method_predicates = pick.method_ty.predicates.instantiate(self.tcx(), &all_substs);
let method_predicates = self.fcx.normalize_associated_types_in(self.span,
&method_predicates);

Expand Down
8 changes: 0 additions & 8 deletions src/librustc_typeck/check/vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,6 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
in the supertrait listing");
}

ObjectSafetyViolation::Method(method, MethodViolationCode::ByValueSelf) => {
tcx.sess.span_note(
span,
&format!("method `{}` has a receiver type of `Self`, \
which cannot be used with a trait object",
method.name.user_string(tcx)));
}

ObjectSafetyViolation::Method(method, MethodViolationCode::StaticMethod) => {
tcx.sess.span_note(
span,
Expand Down
3 changes: 1 addition & 2 deletions src/test/compile-fail/fn-trait-formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ fn needs_fn<F>(x: F) where F: Fn(isize) -> isize {}

fn main() {
let _: () = (box |_: isize| {}) as Box<FnOnce(isize)>;
//~^ ERROR object-safe
//~| ERROR mismatched types
//~^ ERROR mismatched types
//~| expected `()`
//~| found `Box<core::ops::FnOnce(isize)>`
//~| expected ()
Expand Down
6 changes: 5 additions & 1 deletion src/test/compile-fail/issue-18959.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ impl Foo for Thing {
fn foo<T>(&self, _: &T) {}
}

#[inline(never)] fn foo(b: &Bar) { b.foo(&0_usize) }
#[inline(never)]
fn foo(b: &Bar) {
b.foo(&0usize)
//~^ ERROR the trait `Foo` is not implemented for the type `Bar`
}

fn main() {
let mut thing = Thing;
Expand Down
29 changes: 29 additions & 0 deletions src/test/compile-fail/object-safety-by-value-self-use.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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 while a trait with by-value self is object-safe, we
// can't actually invoke it from an object (yet...?).

#![feature(rustc_attrs)]

trait Bar {
fn bar(self);
}

trait Baz {
fn baz(self: Self);
}

fn use_bar(t: Box<Bar>) {
t.bar() //~ ERROR cannot move a value of type Bar
}

fn main() { }

38 changes: 23 additions & 15 deletions src/test/compile-fail/object-safety-by-value-self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
// 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
// from traits with a `fn(self)` method.
// Check that a trait with by-value self is considered object-safe.

#![feature(rustc_attrs)]
#![allow(dead_code)]

trait Bar {
fn bar(self);
Expand All @@ -19,29 +21,35 @@ trait Baz {
fn baz(self: Self);
}

trait Quux {
// Legal because of the where clause:
fn baz(self: Self) where Self : Sized;
}

fn make_bar<T:Bar>(t: &T) -> &Bar {
t
//~^ ERROR `Bar` is not object-safe
//~| NOTE method `bar` has a receiver type of `Self`
t // legal
}

fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
t as &Bar
//~^ ERROR `Bar` is not object-safe
//~| NOTE method `bar` has a receiver type of `Self`
t as &Bar // legal
}

fn make_baz<T:Baz>(t: &T) -> &Baz {
t
//~^ ERROR `Baz` is not object-safe
//~| NOTE method `baz` has a receiver type of `Self`
t // legal
}

fn make_baz_explicit<T:Baz>(t: &T) -> &Baz {
t as &Baz
//~^ ERROR `Baz` is not object-safe
//~| NOTE method `baz` has a receiver type of `Self`
t as &Baz // legal
}

fn make_quux<T:Quux>(t: &T) -> &Quux {
t
}

fn make_quux_explicit<T:Quux>(t: &T) -> &Quux {
t as &Quux
}

fn main() {
#[rustc_error]
fn main() { //~ ERROR compilation successful
}
16 changes: 15 additions & 1 deletion src/test/compile-fail/object-safety-generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
// except according to those terms.

// Check that we correctly prevent users from making trait objects
// from traits with generic methods.
// from traits with generic methods, unless `where Self : Sized` is
// present.

trait Bar {
fn bar<T>(&self, t: T);
}

trait Quux {
fn bar<T>(&self, t: T)
where Self : Sized;
}

fn make_bar<T:Bar>(t: &T) -> &Bar {
t
//~^ ERROR `Bar` is not object-safe
Expand All @@ -27,5 +33,13 @@ fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
//~| NOTE method `bar` has generic type parameters
}

fn make_quux<T:Quux>(t: &T) -> &Quux {
t
}

fn make_quux_explicit<T:Quux>(t: &T) -> &Quux {
t as &Quux
}

fn main() {
}
15 changes: 14 additions & 1 deletion src/test/compile-fail/object-safety-mentions-Self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
// 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.
// form traits that make use of `Self` in an argument or return
// position, unless `where Self : Sized` is present..

trait Bar {
fn bar(&self, x: &Self);
Expand All @@ -19,6 +20,10 @@ trait Baz {
fn bar(&self) -> Self;
}

trait Quux {
fn get(&self, s: &Self) -> Self where Self : Sized;
}

fn make_bar<T:Bar>(t: &T) -> &Bar {
t
//~^ ERROR `Bar` is not object-safe
Expand All @@ -43,5 +48,13 @@ fn make_baz_explicit<T:Baz>(t: &T) -> &Baz {
//~| NOTE method `bar` references the `Self` type in its arguments or return type
}

fn make_quux<T:Quux>(t: &T) -> &Quux {
t
}

fn make_quux_explicit<T:Quux>(t: &T) -> &Quux {
t as &Quux
}

fn main() {
}
1 change: 1 addition & 0 deletions src/test/compile-fail/trait-test-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ fn main() {
10.dup::<i32>(); //~ ERROR does not take type parameters
10.blah::<i32, i32>(); //~ ERROR incorrect number of type parameters
(box 10 as Box<bar>).dup(); //~ ERROR cannot convert to a trait object
//~^ ERROR the trait `bar` is not implemented for the type `bar`
}
46 changes: 46 additions & 0 deletions src/test/run-pass/object-safety-sized-self-by-value-self.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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 a trait is still object-safe (and usable) if it has
// methods with by-value self so long as they require `Self : Sized`.

trait Counter {
fn tick(&mut self) -> u32;
fn get(self) -> u32 where Self : Sized;
}

struct CCounter {
c: u32
}

impl Counter for CCounter {
fn tick(&mut self) -> u32 { self.c += 1; self.c }
fn get(self) -> u32 where Self : Sized { self.c }
}

fn tick1<C:Counter>(mut c: C) -> u32 {
tick2(&mut c);
c.get()
}

fn tick2(c: &mut Counter) {
tick3(c);
}

fn tick3<C:?Sized+Counter>(c: &mut C) {
c.tick();
c.tick();
}

fn main() {
let mut c = CCounter { c: 0 };
let value = tick1(c);
assert_eq!(value, 2);
}
Loading

0 comments on commit 5676f60

Please sign in to comment.