blob: 60e1f96856e0eb9d6cf003cf1c1a036292ceac37 [file] [log] [blame]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly", feature(core_intrinsics, try_from))]
#[cfg(not(feature = "std"))]
mod std {
pub use core::*;
}
use std::any::Any as StdAny;
use std::any::TypeId;
#[cfg(feature = "nightly")]
use std::convert::TryFrom;
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "nightly")]
use std::intrinsics;
use std::fmt::{self, Debug, Display};
use std::mem;
// ++++++++++++++++++++ Any ++++++++++++++++++++
#[cfg(feature = "nightly")]
fn type_name<T: StdAny + ?Sized>() -> &'static str { unsafe { intrinsics::type_name::<T>() } }
#[cfg(not(feature = "nightly"))]
fn type_name<T: StdAny + ?Sized>() -> &'static str { "[ONLY ON NIGHTLY]" }
pub trait Any: StdAny {
/// TODO: once 1.33.0 is the minimum supported compiler version, remove
/// Any::type_id_compat and use StdAny::type_id instead.
/// https://github.com/rust-lang/rust/issues/27745
fn type_id_compat(&self) -> TypeId { TypeId::of::<Self>() }
#[doc(hidden)]
fn type_name(&self) -> &'static str { type_name::<Self>() }
}
impl<T> Any for T where T: StdAny + ?Sized {}
// ++++++++++++++++++++ TypeMismatch ++++++++++++++++++++
#[derive(Debug, Clone, Copy)]
pub struct TypeMismatch {
expected: &'static str,
found: &'static str,
}
impl TypeMismatch {
pub fn new<T, O>(found_obj: &O) -> Self
where T: Any + ?Sized, O: Any + ?Sized
{
TypeMismatch {
expected: type_name::<T>(),
found: found_obj.type_name(),
}
}
}
impl Display for TypeMismatch {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Type mismatch: Expected '{}', found '{}'!", self.expected, self.found)
}
}
#[cfg(feature = "std")]
impl Error for TypeMismatch {
fn description(&self) -> &str { "Type mismatch" }
}
// ++++++++++++++++++++ DowncastError ++++++++++++++++++++
pub struct DowncastError<O> {
mismatch: TypeMismatch,
object: O,
}
impl<O> DowncastError<O> {
pub fn new(mismatch: TypeMismatch, object: O) -> Self {
Self {
mismatch: mismatch,
object: object,
}
}
pub fn type_mismatch(&self) -> TypeMismatch { self.mismatch }
pub fn into_object(self) -> O { self.object }
}
impl<O> Debug for DowncastError<O> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("DowncastError")
.field("mismatch", &self.mismatch)
.finish()
}
}
impl<O> Display for DowncastError<O> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.mismatch, fmt)
}
}
#[cfg(feature = "std")]
impl<O> Error for DowncastError<O> {
fn description(&self) -> &str { self.mismatch.description() }
}
// ++++++++++++++++++++ Downcast ++++++++++++++++++++
#[derive(Clone, Copy)]
struct TraitObject {
pub data: *mut (),
pub vtable: *mut (),
}
#[inline]
fn to_trait_object<T: ?Sized>(obj: &T) -> TraitObject {
assert_eq!(mem::size_of::<&T>(), mem::size_of::<TraitObject>());
unsafe { *((&obj) as *const &T as *const TraitObject) }
}
pub trait Downcast<T>: Any
where T: Any
{
fn is_type(&self) -> bool { self.type_id_compat() == TypeId::of::<T>() }
unsafe fn downcast_ref_unchecked(&self) -> &T { &*(to_trait_object(self).data as *mut T) }
fn downcast_ref(&self) -> Result<&T, TypeMismatch> {
if self.is_type() {
Ok(unsafe { self.downcast_ref_unchecked() })
} else {
Err(TypeMismatch::new::<T, Self>(self))
}
}
unsafe fn downcast_mut_unchecked(&mut self) -> &mut T {
&mut *(to_trait_object(self).data as *mut T)
}
fn downcast_mut(&mut self) -> Result<&mut T, TypeMismatch> {
if self.is_type() {
Ok(unsafe { self.downcast_mut_unchecked() })
} else {
Err(TypeMismatch::new::<T, Self>(self))
}
}
#[cfg(feature = "std")]
unsafe fn downcast_unchecked(self: Box<Self>) -> Box<T> {
let ret: Box<T> = Box::from_raw(to_trait_object(&*self).data as *mut T);
mem::forget(self);
ret
}
#[cfg(feature = "std")]
fn downcast(self: Box<Self>) -> Result<Box<T>, DowncastError<Box<Self>>> {
if self.is_type() {
Ok(unsafe { self.downcast_unchecked() })
} else {
let mismatch = TypeMismatch::new::<T, Self>(&*self);
Err(DowncastError::new(mismatch, self))
}
}
}
// ++++++++++++++++++++ macros ++++++++++++++++++++
#[doc(hidden)]
pub mod _std {
pub use std::*;
}
/// Implements [`Downcast`](trait.Downcast.html) for your trait-object-type.
///
/// ```ignore
/// impl_downcast!(Foo);
/// impl_downcast!(<B> Foo<B> where B: Bar);
/// impl_downcast!(<B> Foo<Bar = B>);
/// ```
///
/// expands to
///
/// ```ignore
/// impl<T> Downcast<T> for Foo
/// where T: Any
/// {}
///
/// impl<T, B> Downcast<T> for Foo<B>
/// where T: Any, B: Bar
/// {}
///
/// impl<T, B> Downcast<T> for Foo<Bar = B>
/// where T: Any
/// {}
/// ```
#[macro_export]
macro_rules! impl_downcast {
(<$($params:ident),+ $(,)*> $base:ty $(where $($bounds:tt)+)*) => {
impl<_T: $crate::Any, $($params),+> $crate::Downcast<_T> for $base
where _T: $crate::Any, $($params: 'static,)* $($($bounds)+)*
{}
};
($base:ty) => {
impl<_T: $crate::Any> $crate::Downcast<_T> for $base
where _T: $crate::Any
{}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! downcast_methods_core {
(@items) => {
#[allow(unused)]
pub fn is<_T>(&self) -> bool
where _T: $crate::Any, Self: $crate::Downcast<_T>
{
$crate::Downcast::<_T>::is_type(self)
}
#[allow(unused)]
pub unsafe fn downcast_ref_unchecked<_T>(&self) -> &_T
where _T: $crate::Any, Self: $crate::Downcast<_T>
{
$crate::Downcast::<_T>::downcast_ref_unchecked(self)
}
#[allow(unused)]
pub fn downcast_ref<_T>(&self) -> $crate::_std::result::Result<&_T, $crate::TypeMismatch>
where _T: $crate::Any, Self: $crate::Downcast<_T>
{
$crate::Downcast::<_T>::downcast_ref(self)
}
#[allow(unused)]
pub unsafe fn downcast_mut_unchecked<_T>(&mut self) -> &mut _T
where _T: $crate::Any, Self: $crate::Downcast<_T>
{
$crate::Downcast::<_T>::downcast_mut_unchecked(self)
}
#[allow(unused)]
pub fn downcast_mut<_T>(&mut self) -> $crate::_std::result::Result<&mut _T, $crate::TypeMismatch>
where _T: $crate::Any, Self: $crate::Downcast<_T>
{
$crate::Downcast::<_T>::downcast_mut(self)
}
};
(<$($params:ident),+ $(,)*> $base:ty $(where $($bounds:tt)+)*) => {
impl<$($params),+> $base
where $($params: 'static,)* $($($bounds)+)*
{
downcast_methods_core!(@items);
}
};
($base:ty) => {
impl $base {
downcast_methods_core!(@items);
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! downcast_methods_std {
(@items) => {
downcast_methods_core!(@items);
#[allow(unused)]
pub unsafe fn downcast_unchecked<_T>(self: $crate::_std::boxed::Box<Self>) -> $crate::_std::boxed::Box<_T>
where _T: $crate::Any, Self: $crate::Downcast<_T>
{
$crate::Downcast::<_T>::downcast_unchecked(self)
}
#[allow(unused)]
pub fn downcast<_T>(self: $crate::_std::boxed::Box<Self>) -> $crate::_std::result::Result<$crate::_std::boxed::Box<_T>, $crate::DowncastError<Box<Self>>>
where _T: $crate::Any, Self: $crate::Downcast<_T>
{
$crate::Downcast::<_T>::downcast(self)
}
};
(<$($params:ident),+ $(,)*> $base:ty $(where $($bounds:tt)+)*) => {
impl<$($params),+> $base
$(where $($bounds)+)*
{
downcast_methods_std!(@items);
}
};
($base:ty) => {
impl $base {
downcast_methods_std!(@items);
}
};
}
/// Generate `downcast`-methods for your trait-object-type.
///
/// ```ignore
/// downcast_methods!(Foo);
/// downcast_methods!(<B> Foo<B> where B: Bar);
/// downcast_methods!(<B> Foo<Bar = B>);
/// ```
///
/// ```ignore
/// /* 1st */ impl Foo {
/// /* 2nd */ impl<B> Foo<B> where B: Bar {
/// /* 3nd */ impl<B> Foo<Bar = B> {
///
/// pub fn is<T>(&self) -> bool
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub unsafe fn downcast_ref_unchecked<T>(&self) -> &T
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub fn downcast_ref<T>(&self) -> Result<&T, TypeMismatch>
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub unsafe fn downcast_mut_unchecked<T>(&mut self) -> &mut T
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub fn downcast_mut<T>(&mut self) -> Result<&mut T, TypeMismatch>
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub unsafe fn downcast_unchecked<T>(self: Box<Self>) -> Box<T>
/// where T: Any, Self: Downcast<T>
/// { ... }
/// }
/// ```
#[cfg(not(feature = "std"))]
#[macro_export]
macro_rules! downcast_methods {
($($tt:tt)+) => { downcast_methods_core!($($tt)+); }
}
/// Generate `downcast`-methods for your trait-object-type.
///
/// ```ignore
/// downcast_methods!(Foo);
/// downcast_methods!(<B> Foo<B> where B: Bar);
/// downcast_methods!(<B> Foo<Bar = B>);
/// ```
///
/// ```ignore
/// /* 1st */ impl Foo {
/// /* 2nd */ impl<B> Foo<B> where B: Bar {
/// /* 3nd */ impl<B> Foo<Bar = B> {
///
/// pub fn is<T>(&self) -> bool
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub unsafe fn downcast_ref_unchecked<T>(&self) -> &T
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub fn downcast_ref<T>(&self) -> Result<&T, TypeMismatch>
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub unsafe fn downcast_mut_unchecked<T>(&mut self) -> &mut T
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub fn downcast_mut<T>(&mut self) -> Result<&mut T, TypeMismatch>
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub unsafe fn downcast_unchecked<T>(self: Box<Self>) -> Box<T>
/// where T: Any, Self: Downcast<T>
/// { ... }
///
/// pub fn downcast<T>(self: Box<Self>) -> Result<Box<T>,
/// DowncastError<Box<T>>>
/// where T: Any, Self: Downcast<T>
/// { ... }
/// }
/// ```
#[cfg(feature = "std")]
#[macro_export]
macro_rules! downcast_methods {
($($tt:tt)+) => { downcast_methods_std!($($tt)+); }
}
/// Implements [`Downcast`](trait.downcast.html) and generates
/// `downcast`-methods for your trait-object-type.
///
/// See [`impl_downcast`](macro.impl_downcast.html),
/// [`downcast_methods`](macro.downcast_methods.html).
#[macro_export]
macro_rules! downcast {
($($tt:tt)+) => {
impl_downcast!($($tt)+);
downcast_methods!($($tt)+);
}
}
// NOTE: We only implement the trait, because implementing the methods won't
// be possible when we replace downcast::Any by std::any::Any.
mod any_impls {
use super::Any;
impl_downcast!(Any);
impl_downcast!((Any + Send));
impl_downcast!((Any + Sync));
impl_downcast!((Any + Send + Sync));
}