about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2015-02-24 07:16:55 +0530
committerManish Goregaokar <manishsmail@gmail.com>2015-02-24 12:08:35 +0530
commit5676f6084bd0a5d1211b3afab93177747bf64fc4 (patch)
treebdf9be9498292bb27f62513130900b331a25d24d
parent8e88762a83ac06450f7455ecc5432b30bfb42f24 (diff)
parentfd9f7da68ed23934ace0ae86b39d47f6078011a0 (diff)
downloadrust-5676f6084bd0a5d1211b3afab93177747bf64fc4.tar.gz
rust-5676f6084bd0a5d1211b3afab93177747bf64fc4.zip
Rollup merge of #22301 - nikomatsakis:object-safe-sized-methods, 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
-rw-r--r--src/librustc/middle/traits/object_safety.rs37
-rw-r--r--src/librustc_trans/trans/meth.rs3
-rw-r--r--src/librustc_typeck/check/method/confirm.rs23
-rw-r--r--src/librustc_typeck/check/vtable.rs8
-rw-r--r--src/test/compile-fail/fn-trait-formatting.rs3
-rw-r--r--src/test/compile-fail/issue-18959.rs6
-rw-r--r--src/test/compile-fail/object-safety-by-value-self-use.rs29
-rw-r--r--src/test/compile-fail/object-safety-by-value-self.rs38
-rw-r--r--src/test/compile-fail/object-safety-generics.rs16
-rw-r--r--src/test/compile-fail/object-safety-mentions-Self.rs15
-rw-r--r--src/test/compile-fail/trait-test-2.rs1
-rw-r--r--src/test/run-pass/object-safety-sized-self-by-value-self.rs46
-rw-r--r--src/test/run-pass/object-safety-sized-self-generic-method.rs46
-rw-r--r--src/test/run-pass/object-safety-sized-self-return-Self.rs47
14 files changed, 252 insertions, 66 deletions
diff --git a/src/librustc/middle/traits/object_safety.rs b/src/librustc/middle/traits/object_safety.rs
index f10f7eb3951..64835a666fa 100644
--- a/src/librustc/middle/traits/object_safety.rs
+++ b/src/librustc/middle/traits/object_safety.rs
@@ -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,
 
@@ -158,18 +155,24 @@ 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 {
@@ -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 => {
         }
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index 7644ecf2cfd..3411f12886d 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -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()
                 }
             }
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 53976df75d6..d7db21f3a2f 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -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);
 
diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs
index 3666b69d1c6..630530cf11f 100644
--- a/src/librustc_typeck/check/vtable.rs
+++ b/src/librustc_typeck/check/vtable.rs
@@ -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,
diff --git a/src/test/compile-fail/fn-trait-formatting.rs b/src/test/compile-fail/fn-trait-formatting.rs
index 71e1f7091b2..d682ef7d70c 100644
--- a/src/test/compile-fail/fn-trait-formatting.rs
+++ b/src/test/compile-fail/fn-trait-formatting.rs
@@ -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 ()
diff --git a/src/test/compile-fail/issue-18959.rs b/src/test/compile-fail/issue-18959.rs
index 368f3c16f51..8fb543fb967 100644
--- a/src/test/compile-fail/issue-18959.rs
+++ b/src/test/compile-fail/issue-18959.rs
@@ -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;
diff --git a/src/test/compile-fail/object-safety-by-value-self-use.rs b/src/test/compile-fail/object-safety-by-value-self-use.rs
new file mode 100644
index 00000000000..1b20a902c9d
--- /dev/null
+++ b/src/test/compile-fail/object-safety-by-value-self-use.rs
@@ -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() { }
+
diff --git a/src/test/compile-fail/object-safety-by-value-self.rs b/src/test/compile-fail/object-safety-by-value-self.rs
index 5ebcc8516ca..976717249e8 100644
--- a/src/test/compile-fail/object-safety-by-value-self.rs
+++ b/src/test/compile-fail/object-safety-by-value-self.rs
@@ -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);
@@ -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
 }
diff --git a/src/test/compile-fail/object-safety-generics.rs b/src/test/compile-fail/object-safety-generics.rs
index 0ca706404c1..fd20accfa1e 100644
--- a/src/test/compile-fail/object-safety-generics.rs
+++ b/src/test/compile-fail/object-safety-generics.rs
@@ -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
@@ -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() {
 }
diff --git a/src/test/compile-fail/object-safety-mentions-Self.rs b/src/test/compile-fail/object-safety-mentions-Self.rs
index df0f44c1391..b546774ccbd 100644
--- a/src/test/compile-fail/object-safety-mentions-Self.rs
+++ b/src/test/compile-fail/object-safety-mentions-Self.rs
@@ -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);
@@ -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
@@ -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() {
 }
diff --git a/src/test/compile-fail/trait-test-2.rs b/src/test/compile-fail/trait-test-2.rs
index d8b3176787c..b09b10ffa0a 100644
--- a/src/test/compile-fail/trait-test-2.rs
+++ b/src/test/compile-fail/trait-test-2.rs
@@ -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`
 }
diff --git a/src/test/run-pass/object-safety-sized-self-by-value-self.rs b/src/test/run-pass/object-safety-sized-self-by-value-self.rs
new file mode 100644
index 00000000000..ae092333134
--- /dev/null
+++ b/src/test/run-pass/object-safety-sized-self-by-value-self.rs
@@ -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);
+}
diff --git a/src/test/run-pass/object-safety-sized-self-generic-method.rs b/src/test/run-pass/object-safety-sized-self-generic-method.rs
new file mode 100644
index 00000000000..1a42c4b6ef6
--- /dev/null
+++ b/src/test/run-pass/object-safety-sized-self-generic-method.rs
@@ -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
+// generic methods so long as they require `Self : Sized`.
+
+trait Counter {
+    fn tick(&mut self) -> u32;
+    fn with<F:FnOnce(u32)>(&self, f: F) where Self : Sized;
+}
+
+struct CCounter {
+    c: u32
+}
+
+impl Counter for CCounter {
+    fn tick(&mut self) -> u32 { self.c += 1; self.c }
+    fn with<F:FnOnce(u32)>(&self, f: F) { f(self.c); }
+}
+
+fn tick1<C:Counter>(c: &mut C) {
+    tick2(c);
+    c.with(|i| ());
+}
+
+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 };
+    tick1(&mut c);
+    assert_eq!(c.tick(), 3);
+}
diff --git a/src/test/run-pass/object-safety-sized-self-return-Self.rs b/src/test/run-pass/object-safety-sized-self-return-Self.rs
new file mode 100644
index 00000000000..7f075bbb6c2
--- /dev/null
+++ b/src/test/run-pass/object-safety-sized-self-return-Self.rs
@@ -0,0 +1,47 @@
+// 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 that return `Self` so long as they require `Self : Sized`.
+
+trait Counter {
+    fn new() -> Self where Self : Sized;
+    fn tick(&mut self) -> u32;
+}
+
+struct CCounter {
+    c: u32
+}
+
+impl Counter for CCounter {
+    fn new() -> CCounter { CCounter { c: 0 } }
+    fn tick(&mut self) -> u32 { self.c += 1; self.c }
+}
+
+fn preticked<C:Counter>() -> C {
+    let mut c: C = Counter::new();
+    tick(&mut c);
+    c
+}
+
+fn tick(c: &mut Counter) {
+    tick_generic(c);
+}
+
+fn tick_generic<C:?Sized+Counter>(c: &mut C) {
+    c.tick();
+    c.tick();
+}
+
+fn main() {
+    let mut c = preticked::<CCounter>();
+    tick(&mut c);
+    assert_eq!(c.tick(), 5);
+}