diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2015-03-24 15:55:29 -0400 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2015-03-26 17:52:38 -0400 |
| commit | 9c9bb9ce1d51e2a9ca4963bd418e365b6e17fbfa (patch) | |
| tree | 9ba4c4fba17c9d7156bd95b0a46dc1c45f4984c9 | |
| parent | a923278c6278c63468d74772c58dbf788e88f58c (diff) | |
| download | rust-9c9bb9ce1d51e2a9ca4963bd418e365b6e17fbfa.tar.gz rust-9c9bb9ce1d51e2a9ca4963bd418e365b6e17fbfa.zip | |
Implement `Reflect` trait with a variant on the standard OIBIT
semantics that tests the *interface* of trait objects, rather than what they close over.
| -rw-r--r-- | src/liballoc/boxed.rs | 6 | ||||
| -rw-r--r-- | src/libcore/any.rs | 24 | ||||
| -rw-r--r-- | src/libcore/lib.rs | 1 | ||||
| -rw-r--r-- | src/libcore/marker.rs | 42 | ||||
| -rw-r--r-- | src/librustc/middle/traits/select.rs | 66 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 7 | ||||
| -rw-r--r-- | src/test/auxiliary/typeid-intrinsic.rs | 4 | ||||
| -rw-r--r-- | src/test/auxiliary/typeid-intrinsic2.rs | 4 | ||||
| -rw-r--r-- | src/test/compile-fail/reflect-assoc.rs | 35 | ||||
| -rw-r--r-- | src/test/compile-fail/reflect-object-param.rs | 47 | ||||
| -rw-r--r-- | src/test/compile-fail/reflect.rs | 39 | ||||
| -rw-r--r-- | src/test/run-pass/object-one-type-two-traits.rs | 2 | ||||
| -rw-r--r-- | src/test/run-pass/type-id-higher-rank.rs | 4 |
13 files changed, 252 insertions, 29 deletions
diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 8b18fbf554a..f9bd0ab2f1e 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -244,13 +244,13 @@ pub trait BoxAny { /// Returns the boxed value if it is of type `T`, or /// `Err(Self)` if it isn't. #[stable(feature = "rust1", since = "1.0.0")] - fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>>; + fn downcast<T: Any>(self) -> Result<Box<T>, Box<Any>>; } #[stable(feature = "rust1", since = "1.0.0")] impl BoxAny for Box<Any> { #[inline] - fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>> { + fn downcast<T: Any>(self) -> Result<Box<T>, Box<Any>> { if self.is::<T>() { unsafe { // Get the raw representation of the trait object @@ -270,7 +270,7 @@ impl BoxAny for Box<Any> { #[stable(feature = "rust1", since = "1.0.0")] impl BoxAny for Box<Any+Send> { #[inline] - fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>> { + fn downcast<T: Any>(self) -> Result<Box<T>, Box<Any>> { <Box<Any>>::downcast(self) } } diff --git a/src/libcore/any.rs b/src/libcore/any.rs index c94d8e2ed0c..d3bc07b173a 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -55,7 +55,7 @@ //! } //! //! // This function wants to log its parameter out prior to doing work with it. -//! fn do_work<T: Debug + 'static>(value: &T) { +//! fn do_work<T: Any + Debug>(value: &T) { //! log(value); //! // ...do some other work //! } @@ -76,7 +76,7 @@ use mem::transmute; use option::Option::{self, Some, None}; use raw::TraitObject; use intrinsics; -use marker::Sized; +use marker::{Reflect, Sized}; /////////////////////////////////////////////////////////////////////////////// // Any trait @@ -88,14 +88,16 @@ use marker::Sized; /// /// [mod]: ../index.html #[stable(feature = "rust1", since = "1.0.0")] -pub trait Any: 'static { +pub trait Any: Reflect + 'static { /// Get the `TypeId` of `self` #[unstable(feature = "core", reason = "this method will likely be replaced by an associated static")] fn get_type_id(&self) -> TypeId; } -impl<T: 'static> Any for T { +impl<T> Any for T + where T: Reflect + 'static +{ fn get_type_id(&self) -> TypeId { TypeId::of::<T>() } } @@ -107,7 +109,7 @@ impl Any { /// Returns true if the boxed type is the same as `T` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn is<T: 'static>(&self) -> bool { + pub fn is<T: Any>(&self) -> bool { // Get TypeId of the type this function is instantiated with let t = TypeId::of::<T>(); @@ -122,7 +124,7 @@ impl Any { /// `None` if it isn't. #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn downcast_ref<T: 'static>(&self) -> Option<&T> { + pub fn downcast_ref<T: Any>(&self) -> Option<&T> { if self.is::<T>() { unsafe { // Get the raw representation of the trait object @@ -140,7 +142,7 @@ impl Any { /// `None` if it isn't. #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> { + pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> { if self.is::<T>() { unsafe { // Get the raw representation of the trait object @@ -159,21 +161,21 @@ impl Any+Send { /// Forwards to the method defined on the type `Any`. #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn is<T: 'static>(&self) -> bool { + pub fn is<T: Any>(&self) -> bool { Any::is::<T>(self) } /// Forwards to the method defined on the type `Any`. #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn downcast_ref<T: 'static>(&self) -> Option<&T> { + pub fn downcast_ref<T: Any>(&self) -> Option<&T> { Any::downcast_ref::<T>(self) } /// Forwards to the method defined on the type `Any`. #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> { + pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> { Any::downcast_mut::<T>(self) } } @@ -202,7 +204,7 @@ impl TypeId { /// instantiated with #[unstable(feature = "core", reason = "may grow a `Reflect` bound soon via marker traits")] - pub fn of<T: ?Sized + 'static>() -> TypeId { + pub fn of<T: ?Sized + Any>() -> TypeId { TypeId { t: unsafe { intrinsics::type_id::<T>() }, } diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index a2b13584270..7225b016e6b 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -72,6 +72,7 @@ #![feature(rustc_attrs)] #![feature(optin_builtin_traits)] #![feature(concat_idents)] +#![feature(reflect)] #[macro_use] mod macros; diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 88c10e3661e..26bb53c6b2d 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -450,3 +450,45 @@ pub struct CovariantType<T>; #[deprecated(since = "1.0.0", reason = "Replace with `PhantomData<Cell<T>>`")] #[lang="invariant_type"] pub struct InvariantType<T>; + +/// A marker trait indicates a type that can be reflected over. This +/// trait is implemented for all types. Its purpose is to ensure that +/// when you write a generic function that will employ reflection, +/// that must be reflected (no pun intended) in the generic bounds of +/// that function. Here is an example: +/// +/// ``` +/// use std::marker::Reflect; +/// use std::any::Any; +/// fn foo<T:Reflect+'static>(x: &T) { +/// let any: &Any = x; +/// if any.is::<u32>() { println!("u32"); } +/// } +/// ``` +/// +/// Without the declaration `T:Reflect`, `foo` would not type check +/// (note: as a matter of style, it would be preferable to to write +/// `T:Any`, because `T:Any` implies `T:Reflect` and `T:'static`, but +/// we use `Reflect` here to show how it works). The `Reflect` bound +/// thus serves to alert `foo`'s caller to the fact that `foo` may +/// behave differently depending on whether `T=u32` or not. In +/// particular, thanks to the `Reflect` bound, callers know that a +/// function declared like `fn bar<T>(...)` will always act in +/// precisely the same way no matter what type `T` is supplied, +/// beacuse there are no bounds declared on `T`. (The ability for a +/// caller to reason about what a function may do based solely on what +/// generic bounds are declared is often called the ["parametricity +/// property"][1].) +/// +/// [1]: http://en.wikipedia.org/wiki/Parametricity +#[rustc_reflect_like] +#[unstable(feature = "core", reason = "requires RFC and more experience")] +pub trait Reflect : MarkerTrait { +} + +#[cfg(stage0)] +impl<T> Reflect for T { } + +#[cfg(not(stage0))] +impl Reflect for .. { } + diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 0d6a1f7df5e..f299dc6aaff 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -138,6 +138,7 @@ enum SelectionCandidate<'tcx> { ParamCandidate(ty::PolyTraitRef<'tcx>), ImplCandidate(ast::DefId), DefaultImplCandidate(ast::DefId), + DefaultImplObjectCandidate(ast::DefId), /// This is a trait matching with a projected type as `Self`, and /// we found an applicable bound in the trait definition. @@ -1160,7 +1161,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if ty::trait_has_default_impl(self.tcx(), def_id) { match self_ty.sty { - ty::ty_trait(..) | + ty::ty_trait(..) => { + // For object types, we don't know what the closed + // over types are. For most traits, this means we + // conservatively say nothing; a candidate may be + // added by `assemble_candidates_from_object_ty`. + // However, for the kind of magic reflect trait, + // we consider it to be implemented even for + // object types, because it just lets you reflect + // onto the object type, not into the object's + // interior. + if ty::has_attr(self.tcx(), def_id, "rustc_reflect_like") { + candidates.vec.push(DefaultImplObjectCandidate(def_id)); + } + } ty::ty_param(..) | ty::ty_projection(..) => { // In these cases, we don't know what the actual @@ -1798,7 +1812,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } DefaultImplCandidate(trait_def_id) => { - let data = try!(self.confirm_default_impl_candidate(obligation, trait_def_id)); + let data = self.confirm_default_impl_candidate(obligation, trait_def_id); + Ok(VtableDefaultImpl(data)) + } + + DefaultImplObjectCandidate(trait_def_id) => { + let data = self.confirm_default_impl_object_candidate(obligation, trait_def_id); Ok(VtableDefaultImpl(data)) } @@ -1927,17 +1946,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds. fn confirm_default_impl_candidate(&mut self, obligation: &TraitObligation<'tcx>, - impl_def_id: ast::DefId) - -> Result<VtableDefaultImplData<PredicateObligation<'tcx>>, - SelectionError<'tcx>> + trait_def_id: ast::DefId) + -> VtableDefaultImplData<PredicateObligation<'tcx>> { debug!("confirm_default_impl_candidate({}, {})", obligation.repr(self.tcx()), - impl_def_id.repr(self.tcx())); + trait_def_id.repr(self.tcx())); let self_ty = self.infcx.shallow_resolve(obligation.predicate.0.self_ty()); match self.constituent_types_for_ty(self_ty) { - Some(types) => Ok(self.vtable_default_impl(obligation, impl_def_id, types)), + Some(types) => self.vtable_default_impl(obligation, trait_def_id, types), None => { self.tcx().sess.bug( &format!( @@ -1947,6 +1965,39 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn confirm_default_impl_object_candidate(&mut self, + obligation: &TraitObligation<'tcx>, + trait_def_id: ast::DefId) + -> VtableDefaultImplData<PredicateObligation<'tcx>> + { + debug!("confirm_default_impl_object_candidate({}, {})", + obligation.repr(self.tcx()), + trait_def_id.repr(self.tcx())); + + assert!(ty::has_attr(self.tcx(), trait_def_id, "rustc_reflect_like")); + + let self_ty = self.infcx.shallow_resolve(obligation.predicate.0.self_ty()); + match self_ty.sty { + ty::ty_trait(ref data) => { + // OK to skip the binder, since vtable_default_impl reintroduces it + let input_types = data.principal.skip_binder().substs.types.get_slice(TypeSpace); + let assoc_types = data.bounds.projection_bounds + .iter() + .map(|pb| pb.skip_binder().ty); + let all_types: Vec<_> = input_types.iter().cloned() + .chain(assoc_types) + .collect(); + self.vtable_default_impl(obligation, trait_def_id, all_types) + } + _ => { + self.tcx().sess.bug( + &format!( + "asked to confirm default object implementation for non-object type: {}", + self_ty.repr(self.tcx()))); + } + } + } + /// See `confirm_default_impl_candidate` fn vtable_default_impl(&mut self, obligation: &TraitObligation<'tcx>, @@ -2530,6 +2581,7 @@ impl<'tcx> Repr<'tcx> for SelectionCandidate<'tcx> { ParamCandidate(ref a) => format!("ParamCandidate({})", a.repr(tcx)), ImplCandidate(a) => format!("ImplCandidate({})", a.repr(tcx)), DefaultImplCandidate(t) => format!("DefaultImplCandidate({:?})", t), + DefaultImplObjectCandidate(t) => format!("DefaultImplObjectCandidate({:?})", t), ProjectionCandidate => format!("ProjectionCandidate"), FnPointerCandidate => format!("FnPointerCandidate"), ObjectCandidate => format!("ObjectCandidate"), diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 60f81dac1e9..46115ae468f 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -74,6 +74,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ ("rustc_diagnostic_macros", "1.0.0", Active), ("unboxed_closures", "1.0.0", Active), + ("reflect", "1.0.0", Active), ("import_shadowing", "1.0.0", Removed), ("advanced_slice_patterns", "1.0.0", Active), ("tuple_indexing", "1.0.0", Accepted), @@ -281,7 +282,11 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ // FIXME: #19470 this shouldn't be needed forever ("old_orphan_check", Whitelisted), ("old_impl_check", Whitelisted), - ("rustc_paren_sugar", Whitelisted), // FIXME: #18101 temporary unboxed closure hack + + ("rustc_paren_sugar", Gated("unboxed_closures", + "unboxed_closures are still evolving")), + ("rustc_reflect_like", Gated("reflect", + "defining reflective traits is still evolving")), // Crate level attributes ("crate_name", CrateLevel), diff --git a/src/test/auxiliary/typeid-intrinsic.rs b/src/test/auxiliary/typeid-intrinsic.rs index 82d07a9df4e..bd47054f093 100644 --- a/src/test/auxiliary/typeid-intrinsic.rs +++ b/src/test/auxiliary/typeid-intrinsic.rs @@ -10,7 +10,7 @@ #![feature(core)] -use std::any::TypeId; +use std::any::{Any, TypeId}; pub struct A; pub struct B(Option<A>); @@ -31,4 +31,4 @@ pub unsafe fn id_F() -> TypeId { TypeId::of::<F>() } pub unsafe fn id_G() -> TypeId { TypeId::of::<G>() } pub unsafe fn id_H() -> TypeId { TypeId::of::<H>() } -pub unsafe fn foo<T: 'static>() -> TypeId { TypeId::of::<T>() } +pub unsafe fn foo<T: Any>() -> TypeId { TypeId::of::<T>() } diff --git a/src/test/auxiliary/typeid-intrinsic2.rs b/src/test/auxiliary/typeid-intrinsic2.rs index 82d07a9df4e..5e81bf50ae4 100644 --- a/src/test/auxiliary/typeid-intrinsic2.rs +++ b/src/test/auxiliary/typeid-intrinsic2.rs @@ -10,7 +10,7 @@ #![feature(core)] -use std::any::TypeId; +use std::any::{Any, TypeId}; pub struct A; pub struct B(Option<A>); @@ -31,4 +31,4 @@ pub unsafe fn id_F() -> TypeId { TypeId::of::<F>() } pub unsafe fn id_G() -> TypeId { TypeId::of::<G>() } pub unsafe fn id_H() -> TypeId { TypeId::of::<H>() } -pub unsafe fn foo<T: 'static>() -> TypeId { TypeId::of::<T>() } +pub unsafe fn foo<T:Any>() -> TypeId { TypeId::of::<T>() } diff --git a/src/test/compile-fail/reflect-assoc.rs b/src/test/compile-fail/reflect-assoc.rs new file mode 100644 index 00000000000..9cf0d252c2d --- /dev/null +++ b/src/test/compile-fail/reflect-assoc.rs @@ -0,0 +1,35 @@ +// Copyright 2015 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. + +// Test that types that appear in assoc bindings in an object +// type are subject to the reflect check. + +use std::marker::Reflect; +use std::io::Write; + +trait Get { + type Output; + fn get(self) -> Self::Output; +} + +struct Struct<T>(T); + +fn is_reflect<T:Reflect>() { } + +fn a<T>() { + is_reflect::<Box<Get<Output=T>>>(); //~ ERROR not implemented +} + +fn ok_a<T: Reflect>() { + is_reflect::<Box<Get<Output=T>>>(); // OK +} + +fn main() { +} diff --git a/src/test/compile-fail/reflect-object-param.rs b/src/test/compile-fail/reflect-object-param.rs new file mode 100644 index 00000000000..9f074667feb --- /dev/null +++ b/src/test/compile-fail/reflect-object-param.rs @@ -0,0 +1,47 @@ +// Copyright 2015 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. + +// Test that types that appear in input types in an object type are +// subject to the reflect check. + +use std::marker::Reflect; +use std::io::Write; + +trait Get<T> { + fn get(self) -> T; +} + +struct Struct<T>(T); + +fn is_reflect<T:Reflect>() { } + +fn a<T>() { + is_reflect::<T>(); //~ ERROR not implemented +} + +fn ok_a<T: Reflect>() { + is_reflect::<T>(); // OK +} + +fn b<T>() { + is_reflect::<Box<Get<T>>>(); //~ ERROR not implemented +} + +fn ok_b<T: Reflect>() { + is_reflect::<Box<Get<T>>>(); // OK +} + +fn c<T>() { + is_reflect::<Box<Get<Struct<T>>>>(); //~ ERROR not implemented +} + +fn main() { + is_reflect::<Box<Get<Struct<()>>>>(); // OK +} diff --git a/src/test/compile-fail/reflect.rs b/src/test/compile-fail/reflect.rs new file mode 100644 index 00000000000..701aa5b40bc --- /dev/null +++ b/src/test/compile-fail/reflect.rs @@ -0,0 +1,39 @@ +// Copyright 2015 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. + +// Test that there is no way to get a generic type `T` to be +// considered as `Reflect` (or accessible via something that is +// considered `Reflect`) without a reflect bound, but that any +// concrete type works fine. Note that object types are tested +// separately. + +use std::marker::Reflect; +use std::io::Write; + +struct Struct<T>(T); + +fn is_reflect<T:Reflect>() { } + +fn c<T>() { + is_reflect::<Struct<T>>(); //~ ERROR not implemented +} + +fn ok_c<T: Reflect>() { + is_reflect::<Struct<T>>(); // OK +} + +fn d<T>() { + is_reflect::<(i32, T)>(); //~ ERROR not implemented +} + +fn main() { + is_reflect::<&i32>(); // OK + is_reflect::<Box<Write>>(); // OK +} diff --git a/src/test/run-pass/object-one-type-two-traits.rs b/src/test/run-pass/object-one-type-two-traits.rs index baf8c6e4c97..f4e056b3f21 100644 --- a/src/test/run-pass/object-one-type-two-traits.rs +++ b/src/test/run-pass/object-one-type-two-traits.rs @@ -30,7 +30,7 @@ impl Wrap for int { } } -fn is<T:'static>(x: &Any) -> bool { +fn is<T:Any>(x: &Any) -> bool { x.is::<T>() } diff --git a/src/test/run-pass/type-id-higher-rank.rs b/src/test/run-pass/type-id-higher-rank.rs index 5670c45b68a..a40989d4e37 100644 --- a/src/test/run-pass/type-id-higher-rank.rs +++ b/src/test/run-pass/type-id-higher-rank.rs @@ -15,7 +15,7 @@ #![feature(unboxed_closures, core)] -use std::any::TypeId; +use std::any::{Any, TypeId}; fn main() { // Bare fns @@ -63,7 +63,7 @@ fn main() { assert!(a != b); } - fn id<T:'static>(_: T) -> TypeId { + fn id<T:Any>(_: T) -> TypeId { TypeId::of::<T>() } } |
