From 8f5d22593328d18bd140e7ab10bdeccd0690928a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 9 Feb 2015 08:54:34 -0500 Subject: [PATCH] Extend object safety so that methods with Sized:Self are exempt. --- src/librustc/middle/traits/object_safety.rs | 24 +++++++--- src/librustc_trans/trans/meth.rs | 3 -- .../object-safety-by-value-self.rs | 16 ++++++- .../compile-fail/object-safety-generics.rs | 16 ++++++- .../object-safety-mentions-Self.rs | 15 +++++- .../object-safety-sized-self-by-value-self.rs | 46 ++++++++++++++++++ ...object-safety-sized-self-generic-method.rs | 46 ++++++++++++++++++ .../object-safety-sized-self-return-Self.rs | 47 +++++++++++++++++++ 8 files changed, 201 insertions(+), 12 deletions(-) create mode 100644 src/test/run-pass/object-safety-sized-self-by-value-self.rs create mode 100644 src/test/run-pass/object-safety-sized-self-generic-method.rs create mode 100644 src/test/run-pass/object-safety-sized-self-return-Self.rs diff --git a/src/librustc/middle/traits/object_safety.rs b/src/librustc/middle/traits/object_safety.rs index f10f7eb3951..76568280282 100644 --- a/src/librustc/middle/traits/object_safety.rs +++ b/src/librustc/middle/traits/object_safety.rs @@ -157,6 +157,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 +174,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,6 +198,12 @@ fn object_safety_violations_for_method<'tcx>(tcx: &ty::ctxt<'tcx>, method: &ty::Method<'tcx>) -> Option { + // 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 to // `&self`. For now, we only accept `&self` and `Box`. match method.explicit_self { diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 65d8f8ec361..643649cceda 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/test/compile-fail/object-safety-by-value-self.rs b/src/test/compile-fail/object-safety-by-value-self.rs index 5ebcc8516ca..71dedbe2799 100644 --- a/src/test/compile-fail/object-safety-by-value-self.rs +++ b/src/test/compile-fail/object-safety-by-value-self.rs @@ -9,7 +9,8 @@ // except according to those terms. // Check that we correctly prevent users from making trait objects -// from traits with a `fn(self)` method. +// from traits with a `fn(self)` method, unless `where Self : Sized` +// is present on the method. trait Bar { fn bar(self); @@ -19,6 +20,11 @@ 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 @@ -43,5 +49,13 @@ fn make_baz_explicit(t: &T) -> &Baz { //~| NOTE method `baz` has a receiver type of `Self` } +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-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(&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 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: &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/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 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 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 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 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 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); +}