Implement Reflect
trait with a variant on the standard OIBIT
semantics that tests the *interface* of trait objects, rather than what they close over.
This commit is contained in:
parent
a923278c62
commit
9c9bb9ce1d
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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>() },
|
||||
}
|
||||
|
@ -72,6 +72,7 @@
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(optin_builtin_traits)]
|
||||
#![feature(concat_idents)]
|
||||
#![feature(reflect)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
@ -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 .. { }
|
||||
|
||||
|
@ -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"),
|
||||
|
@ -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),
|
||||
|
@ -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>() }
|
||||
|
@ -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>() }
|
||||
|
35
src/test/compile-fail/reflect-assoc.rs
Normal file
35
src/test/compile-fail/reflect-assoc.rs
Normal file
@ -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() {
|
||||
}
|
47
src/test/compile-fail/reflect-object-param.rs
Normal file
47
src/test/compile-fail/reflect-object-param.rs
Normal file
@ -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
|
||||
}
|
39
src/test/compile-fail/reflect.rs
Normal file
39
src/test/compile-fail/reflect.rs
Normal file
@ -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
|
||||
}
|
@ -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>()
|
||||
}
|
||||
|
||||
|
@ -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>()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user