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:
Niko Matsakis 2015-03-24 15:55:29 -04:00
parent a923278c62
commit 9c9bb9ce1d
13 changed files with 252 additions and 29 deletions

View File

@ -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)
}
}

View File

@ -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>() },
}

View File

@ -72,6 +72,7 @@
#![feature(rustc_attrs)]
#![feature(optin_builtin_traits)]
#![feature(concat_idents)]
#![feature(reflect)]
#[macro_use]
mod macros;

View File

@ -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 .. { }

View File

@ -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"),

View File

@ -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),

View File

@ -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>() }

View File

@ -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>() }

View 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() {
}

View 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
}

View 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
}

View File

@ -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>()
}

View File

@ -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>()
}
}