diff --git a/src/librustc/middle/traits/object_safety.rs b/src/librustc/middle/traits/object_safety.rs index f10f7eb3951c7..64835a666faef 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, @@ -157,6 +154,16 @@ 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, @@ -164,12 +171,8 @@ fn trait_has_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>, }; // 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 { - // The method's first parameter must be something that derefs to - // `&self`. For now, we only accept `&self` and `Box`. - 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`. + 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 65d8f8ec3614d..643649cceda53 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -815,9 +815,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 53976df75d616..d7db21f3a2f76 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 3666b69d1c678..630530cf11f84 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 71e1f7091b2c9..d682ef7d70c9d 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(x: F) where F: Fn(isize) -> isize {} fn main() { let _: () = (box |_: isize| {}) as Box; - //~^ ERROR object-safe - //~| ERROR mismatched types + //~^ ERROR mismatched types //~| expected `()` //~| found `Box` //~| expected () diff --git a/src/test/compile-fail/issue-18959.rs b/src/test/compile-fail/issue-18959.rs index 368f3c16f5188..8fb543fb96703 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(&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 0000000000000..1b20a902c9d52 --- /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 or the MIT license +// , 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) { + 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 5ebcc8516ca05..976717249e8eb 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: &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: &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: &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: &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: &T) -> &Quux { + t +} + +fn make_quux_explicit(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 0ca706404c1f3..fd20accfa1e6b 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(&self, t: T); } +trait Quux { + fn bar(&self, t: T) + where Self : Sized; +} + fn make_bar(t: &T) -> &Bar { t //~^ ERROR `Bar` is not object-safe @@ -27,5 +33,13 @@ fn make_bar_explicit(t: &T) -> &Bar { //~| NOTE method `bar` has generic type parameters } +fn make_quux(t: &T) -> &Quux { + t +} + +fn make_quux_explicit(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 df0f44c139158..b546774ccbd8c 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: &T) -> &Bar { t //~^ ERROR `Bar` is not object-safe @@ -43,5 +48,13 @@ fn make_baz_explicit(t: &T) -> &Baz { //~| NOTE method `bar` references the `Self` type in its arguments or return type } +fn make_quux(t: &T) -> &Quux { + t +} + +fn make_quux_explicit(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 d8b3176787c04..b09b10ffa0aad 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::(); //~ ERROR does not take type parameters 10.blah::(); //~ ERROR incorrect number of type parameters (box 10 as Box).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 0000000000000..ae092333134ea --- /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 or the MIT license +// , 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(mut c: C) -> u32 { + tick2(&mut c); + c.get() +} + +fn tick2(c: &mut Counter) { + tick3(c); +} + +fn tick3(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 0000000000000..1a42c4b6ef664 --- /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 or the MIT license +// , 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(&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(&self, f: F) { f(self.c); } +} + +fn tick1(c: &mut C) { + tick2(c); + c.with(|i| ()); +} + +fn tick2(c: &mut Counter) { + tick3(c); +} + +fn tick3(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 0000000000000..7f075bbb6c2f2 --- /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 or the MIT license +// , 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 { + let mut c: C = Counter::new(); + tick(&mut c); + c +} + +fn tick(c: &mut Counter) { + tick_generic(c); +} + +fn tick_generic(c: &mut C) { + c.tick(); + c.tick(); +} + +fn main() { + let mut c = preticked::(); + tick(&mut c); + assert_eq!(c.tick(), 5); +}