From 9c9bb9ce1d51e2a9ca4963bd418e365b6e17fbfa Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 24 Mar 2015 15:55:29 -0400 Subject: [PATCH] Implement `Reflect` trait with a variant on the standard OIBIT semantics that tests the *interface* of trait objects, rather than what they close over. --- src/liballoc/boxed.rs | 6 +- src/libcore/any.rs | 24 +++---- src/libcore/lib.rs | 1 + src/libcore/marker.rs | 42 ++++++++++++ src/librustc/middle/traits/select.rs | 66 +++++++++++++++++-- src/libsyntax/feature_gate.rs | 7 +- src/test/auxiliary/typeid-intrinsic.rs | 4 +- src/test/auxiliary/typeid-intrinsic2.rs | 4 +- src/test/compile-fail/reflect-assoc.rs | 35 ++++++++++ src/test/compile-fail/reflect-object-param.rs | 47 +++++++++++++ src/test/compile-fail/reflect.rs | 39 +++++++++++ .../run-pass/object-one-type-two-traits.rs | 2 +- src/test/run-pass/type-id-higher-rank.rs | 4 +- 13 files changed, 252 insertions(+), 29 deletions(-) create mode 100644 src/test/compile-fail/reflect-assoc.rs create mode 100644 src/test/compile-fail/reflect-object-param.rs create mode 100644 src/test/compile-fail/reflect.rs diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 8b18fbf554a4..f9bd0ab2f1e0 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(self) -> Result, Box>; + fn downcast(self) -> Result, Box>; } #[stable(feature = "rust1", since = "1.0.0")] impl BoxAny for Box { #[inline] - fn downcast(self) -> Result, Box> { + fn downcast(self) -> Result, Box> { if self.is::() { unsafe { // Get the raw representation of the trait object @@ -270,7 +270,7 @@ impl BoxAny for Box { #[stable(feature = "rust1", since = "1.0.0")] impl BoxAny for Box { #[inline] - fn downcast(self) -> Result, Box> { + fn downcast(self) -> Result, Box> { >::downcast(self) } } diff --git a/src/libcore/any.rs b/src/libcore/any.rs index c94d8e2ed0c8..d3bc07b173ac 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(value: &T) { +//! fn do_work(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 Any for T { +impl Any for T + where T: Reflect + 'static +{ fn get_type_id(&self) -> TypeId { TypeId::of::() } } @@ -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(&self) -> bool { + pub fn is(&self) -> bool { // Get TypeId of the type this function is instantiated with let t = TypeId::of::(); @@ -122,7 +124,7 @@ impl Any { /// `None` if it isn't. #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn downcast_ref(&self) -> Option<&T> { + pub fn downcast_ref(&self) -> Option<&T> { if self.is::() { 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(&mut self) -> Option<&mut T> { + pub fn downcast_mut(&mut self) -> Option<&mut T> { if self.is::() { 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(&self) -> bool { + pub fn is(&self) -> bool { Any::is::(self) } /// Forwards to the method defined on the type `Any`. #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn downcast_ref(&self) -> Option<&T> { + pub fn downcast_ref(&self) -> Option<&T> { Any::downcast_ref::(self) } /// Forwards to the method defined on the type `Any`. #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn downcast_mut(&mut self) -> Option<&mut T> { + pub fn downcast_mut(&mut self) -> Option<&mut T> { Any::downcast_mut::(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() -> TypeId { + pub fn of() -> TypeId { TypeId { t: unsafe { intrinsics::type_id::() }, } diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index a2b135842709..7225b016e6ba 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 88c10e3661e7..26bb53c6b2db 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -450,3 +450,45 @@ pub struct CovariantType; #[deprecated(since = "1.0.0", reason = "Replace with `PhantomData>`")] #[lang="invariant_type"] pub struct InvariantType; + +/// 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(x: &T) { +/// let any: &Any = x; +/// if any.is::() { 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(...)` 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 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 0d6a1f7df5e5..f299dc6aaff1 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>, - SelectionError<'tcx>> + trait_def_id: ast::DefId) + -> VtableDefaultImplData> { 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> + { + 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 60f81dac1e9a..46115ae468ff 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 82d07a9df4e1..bd47054f093c 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); @@ -31,4 +31,4 @@ pub unsafe fn id_F() -> TypeId { TypeId::of::() } pub unsafe fn id_G() -> TypeId { TypeId::of::() } pub unsafe fn id_H() -> TypeId { TypeId::of::() } -pub unsafe fn foo() -> TypeId { TypeId::of::() } +pub unsafe fn foo() -> TypeId { TypeId::of::() } diff --git a/src/test/auxiliary/typeid-intrinsic2.rs b/src/test/auxiliary/typeid-intrinsic2.rs index 82d07a9df4e1..5e81bf50ae44 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); @@ -31,4 +31,4 @@ pub unsafe fn id_F() -> TypeId { TypeId::of::() } pub unsafe fn id_G() -> TypeId { TypeId::of::() } pub unsafe fn id_H() -> TypeId { TypeId::of::() } -pub unsafe fn foo() -> TypeId { TypeId::of::() } +pub unsafe fn foo() -> TypeId { TypeId::of::() } diff --git a/src/test/compile-fail/reflect-assoc.rs b/src/test/compile-fail/reflect-assoc.rs new file mode 100644 index 000000000000..9cf0d252c2d5 --- /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 or the MIT license +// , 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); + +fn is_reflect() { } + +fn a() { + is_reflect::>>(); //~ ERROR not implemented +} + +fn ok_a() { + is_reflect::>>(); // 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 000000000000..9f074667feb3 --- /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 or the MIT license +// , 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 { + fn get(self) -> T; +} + +struct Struct(T); + +fn is_reflect() { } + +fn a() { + is_reflect::(); //~ ERROR not implemented +} + +fn ok_a() { + is_reflect::(); // OK +} + +fn b() { + is_reflect::>>(); //~ ERROR not implemented +} + +fn ok_b() { + is_reflect::>>(); // OK +} + +fn c() { + is_reflect::>>>(); //~ ERROR not implemented +} + +fn main() { + is_reflect::>>>(); // OK +} diff --git a/src/test/compile-fail/reflect.rs b/src/test/compile-fail/reflect.rs new file mode 100644 index 000000000000..701aa5b40bc0 --- /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 or the MIT license +// , 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); + +fn is_reflect() { } + +fn c() { + is_reflect::>(); //~ ERROR not implemented +} + +fn ok_c() { + is_reflect::>(); // OK +} + +fn d() { + is_reflect::<(i32, T)>(); //~ ERROR not implemented +} + +fn main() { + is_reflect::<&i32>(); // OK + is_reflect::>(); // 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 baf8c6e4c979..f4e056b3f21b 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(x: &Any) -> bool { +fn is(x: &Any) -> bool { x.is::() } diff --git a/src/test/run-pass/type-id-higher-rank.rs b/src/test/run-pass/type-id-higher-rank.rs index 5670c45b68ad..a40989d4e37f 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) -> TypeId { + fn id(_: T) -> TypeId { TypeId::of::() } }