Skip to content

Commit

Permalink
rollup merge of rust-lang#23712: nikomatsakis/reflect-trait
Browse files Browse the repository at this point in the history
This PR introduces a `Reflect` marker trait which is a supertrait of `Any`. The idea is that `Reflect` is defined for all concrete types, but is not defined for type parameters unless there is a `T:Reflect` bound. This is intended to preserve the parametricity property. This allows the `Any` interface to be stabilized without committing us to unbounded reflection that is not easily detectable by the caller.

The implementation of `Reflect` relies on an experimental variant of OIBIT. This variant behaves differently for objects, since it requires that all types exposed as part of the object's *interface* are `Reflect`, but isn't concerned about other types that may be closed over. In other words, you don't have to write `Foo+Reflect` in order for `Foo: Reflect` to hold (where `Foo` is a trait).

Given that `Any` is slated to stabilization and hence that we are committed to some form of reflection, the goal of this PR is to leave our options open with respect to parametricity. I see the options for full stabilization as follows (I think an RFC would be an appropriate way to confirm whichever of these three routes we take):

1. We make `Reflect` a lang-item.
2. We stabilize some version of the OIBIT variation I implemented as a general mechanism that may be appropriate for other use cases.
3. We give up on preserving parametricity here and just have `impl<T> Reflect for T` instead. In that case, `Reflect` is a harmless but not especially useful trait going forward.

cc @aturon
cc @alexcrichton
cc @glaebhoerl (this is more-or-less your proposal, as I understood it)
cc @reem (this is more-or-less what we discussed on IRC at some point)
cc @flaper87 (vaguely pertains to OIBIT)
  • Loading branch information
alexcrichton committed Mar 27, 2015
2 parents adbb516 + dd8cf92 commit e6166b7
Show file tree
Hide file tree
Showing 21 changed files with 568 additions and 173 deletions.
6 changes: 3 additions & 3 deletions src/liballoc/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}
Expand Down
24 changes: 13 additions & 11 deletions src/libcore/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
//! }
Expand All @@ -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
Expand All @@ -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>() }
}

Expand All @@ -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>();

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
}
}
Expand Down Expand Up @@ -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>() },
}
Expand Down
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
#![feature(rustc_attrs)]
#![feature(optin_builtin_traits)]
#![feature(concat_idents)]
#![feature(reflect)]

#[macro_use]
mod macros;
Expand Down
43 changes: 43 additions & 0 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,46 @@ 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:
///
/// ```
/// #![feature(core)]
/// 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 .. { }

2 changes: 2 additions & 0 deletions src/librustc/middle/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
// debug output much nicer to read and so on.
let obligation = infcx.resolve_type_vars_if_possible(&obligation);

assert!(!obligation.has_escaping_regions());

if !self.duplicate_set.insert(obligation.predicate.clone()) {
debug!("register_predicate({}) -- already seen, skip", obligation.repr(infcx.tcx));
return;
Expand Down
6 changes: 4 additions & 2 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub use self::util::get_vtable_index_of_object_method;
pub use self::util::trait_ref_for_builtin_bound;
pub use self::util::supertraits;
pub use self::util::Supertraits;
pub use self::util::supertrait_def_ids;
pub use self::util::SupertraitDefIds;
pub use self::util::transitive_bounds;
pub use self::util::upcast;

Expand Down Expand Up @@ -640,7 +642,7 @@ impl<'tcx> FulfillmentError<'tcx> {
}

impl<'tcx> TraitObligation<'tcx> {
fn self_ty(&self) -> Ty<'tcx> {
self.predicate.0.self_ty()
fn self_ty(&self) -> ty::Binder<Ty<'tcx>> {
ty::Binder(self.predicate.skip_binder().self_ty())
}
}
16 changes: 8 additions & 8 deletions src/librustc/middle/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,36 +53,36 @@ pub enum MethodViolationCode {
}

pub fn is_object_safe<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>)
trait_def_id: ast::DefId)
-> bool
{
// Because we query yes/no results frequently, we keep a cache:
let cached_result =
tcx.object_safety_cache.borrow().get(&trait_ref.def_id()).cloned();
tcx.object_safety_cache.borrow().get(&trait_def_id).cloned();

let result =
cached_result.unwrap_or_else(|| {
let result = object_safety_violations(tcx, trait_ref.clone()).is_empty();
let result = object_safety_violations(tcx, trait_def_id).is_empty();

// Record just a yes/no result in the cache; this is what is
// queried most frequently. Note that this may overwrite a
// previous result, but always with the same thing.
tcx.object_safety_cache.borrow_mut().insert(trait_ref.def_id(), result);
tcx.object_safety_cache.borrow_mut().insert(trait_def_id, result);

result
});

debug!("is_object_safe({}) = {}", trait_ref.repr(tcx), result);
debug!("is_object_safe({}) = {}", trait_def_id.repr(tcx), result);

result
}

pub fn object_safety_violations<'tcx>(tcx: &ty::ctxt<'tcx>,
sub_trait_ref: ty::PolyTraitRef<'tcx>)
trait_def_id: ast::DefId)
-> Vec<ObjectSafetyViolation<'tcx>>
{
supertraits(tcx, sub_trait_ref)
.flat_map(|tr| object_safety_violations_for_trait(tcx, tr.def_id()).into_iter())
traits::supertrait_def_ids(tcx, trait_def_id)
.flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id).into_iter())
.collect()
}

Expand Down
Loading

0 comments on commit e6166b7

Please sign in to comment.