use core::{
marker::{PhantomData, PhantomPinned},
mem::{ManuallyDrop, MaybeUninit},
};
pub trait TypeNameable<'a, 'lt>: 'a {
type Name: ?Sized + TypeName<'a, 'lt, Nameable = Self>;
}
pub trait TypeName<'a, 'lt>: 'static {
type Nameable: ?Sized + TypeNameable<'a, 'lt, Name = Self>;
}
#[doc(hidden)]
#[macro_export]
macro_rules! nameable {
{
$vis:vis [$a:lifetime, $lt:lifetime $(, $($generic:ident)*)?]
$type:ty
where {$($bound:tt)*}
} => {
$crate::any::nameable! {
$vis [$a, $lt $(, $($generic)*)?]
$type
where {$($bound)*}
$type
where {$($bound)*}
}
};
{
$vis:vis [$a:lifetime, $lt:lifetime $(, $($generic:ident)*)?]
$type:ty
where {$($bound:tt)*}
$type2:ty
where {$($bound2:tt)*}
} => {
const _: () = {
impl<$a, $lt $(, $($generic: $crate::any::TypeNameable<$a, $lt>)*)?>
$crate::any::TypeNameable<$a, $lt> for $type
where
$($bound)*
{
type Name = Name$(<$($generic::Name),*>)?;
}
$vis struct Name$(< $($generic: ?Sized),* >)?(
::core::marker::PhantomData<fn() -> ($( $(*const $generic,)* )?)>
);
impl<$a, $lt $(, $($generic: $crate::any::TypeName<$a, $lt>)*)?>
$crate::any::TypeName<$a, $lt> for Name$(<$($generic),*>)?
where
$($bound2)*
{
type Nameable = $type2;
}
};
};
}
#[doc(inline)]
pub use nameable;
pub struct Owned<T: ?Sized>(pub T);
nameable! {
pub ['a, 'lt, T]
Owned<T> where {T: ?Sized}
Owned<T::Nameable> where {T: ?Sized}
}
// pub struct Borrowed<'lt, T: ?Sized>(pub &'lt T);
// nameable!(['a, 'lt: 'a, T: ?Sized + 'static]: Borrowed<'lt, T> => Borrowed<'static, T>);
//
// pub struct MutBorrowed<'lt, T: ?Sized>(pub &'lt mut T);
// nameable!(['a, 'lt: 'a, T: ?Sized + 'static]: MutBorrowed<'lt, T> => MutBorrowed<'static, T>);
// box here
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)]
pub struct LtTypeId<'lt> {
_marker: PhantomData<fn(&'lt ()) -> &'lt ()>,
name_id: core::any::TypeId,
}
impl<'lt> LtTypeId<'lt> {
pub fn of<'a, T: ?Sized + TypeNameable<'a, 'lt>>() -> Self {
LtTypeId {
_marker: PhantomData,
name_id: core::any::TypeId::of::<T::Name>(),
}
}
}
pub unsafe trait LtAny<'lt> {
fn type_id(&self) -> LtTypeId<'lt>;
}
unsafe impl<'a, 'lt, T: ?Sized + TypeNameable<'a, 'lt>> LtAny<'lt> for T {
fn type_id(&self) -> LtTypeId<'lt> {
LtTypeId::of::<T>()
}
}
impl<'a, 'lt> dyn LtAny<'lt> + 'a {
pub fn is<T: ?Sized + TypeNameable<'a, 'lt>>(&self) -> bool {
LtTypeId::of::<T>() == self.type_id()
}
pub fn downcast_ref<T: TypeNameable<'a, 'lt>>(&self) -> Option<&T> {
if self.is::<T>() {
Some(unsafe { &*(self as *const dyn LtAny<'lt> as *const T) })
} else {
None
}
}
pub fn downcast_mut<T: TypeNameable<'a, 'lt>>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
Some(unsafe { &mut *(self as *mut dyn LtAny<'lt> as *mut T) })
} else {
None
}
}
#[cfg(feature = "alloc")]
pub fn downcast_box<T: TypeNameable<'a, 'lt>>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
if self.is::<T>() {
Ok(unsafe {
let raw: *mut dyn LtAny<'lt> = Box::into_raw(self);
Box::from_raw(raw as *mut T)
})
} else {
Err(self)
}
}
}
pub trait AnyTrait<'lt> {
fn upcast_to_id<'a>(&'a self, id: LtTypeId<'lt>) -> Option<IndirectLtAny<'a, 'lt, Ref>>
where
'lt: 'a;
fn upcast_to_id_mut<'a>(&'a mut self, id: LtTypeId<'lt>) -> Option<IndirectLtAny<'a, 'lt, Mut>>
where
'lt: 'a;
}
impl<'lt> dyn AnyTrait<'lt> {
pub fn upcast<'a, Trait: ?Sized + TypeNameable<'a, 'lt>>(&'a self) -> Option<&'a Trait> {
self.upcast_to_id(LtTypeId::of::<Trait>())
.map(|object| match object.downcast::<Trait>() {
Ok(object) => object,
Err(object) => panic!(
"Unexpected trait object. This means a bad impl of \
`upcast_to_id`. Expected: {:?}, Got {:?}",
LtTypeId::of::<Trait>(),
object.id()
),
})
}
pub fn upcast_mut<'a, Trait: ?Sized + TypeNameable<'a, 'lt>>(&'a mut self) -> Option<&'a mut Trait> {
self.upcast_to_id_mut(LtTypeId::of::<Trait>())
.map(|object| match object.downcast::<Trait>() {
Ok(object) => object,
Err(object) => panic!(
"Unexpected trait object. This means a bad impl of \
`upcast_to_id`. Expected: {:?}, Got {:?}",
LtTypeId::of::<Trait>(),
object.id()
),
})
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! any_trait {
{
impl[$a:lifetime, $lt:lifetime $($generic:tt)*] $name:ty = [$($protocol:ty),* $(,)?];
} => {
impl<$lt $($generic)*> $crate::any::AnyTrait<$lt> for $name {
#[inline]
fn upcast_to_id<$a>(
&$a self,
id: $crate::any::LtTypeId<$lt>
) -> ::core::option::Option<$crate::any::IndirectLtAny<$a, $lt, $crate::any::Ref>>
where
$lt: $a
{
match id {
$(id if id == $crate::any::LtTypeId::of::<$protocol>()
=> ::core::option::Option::Some($crate::any::IndirectLtAny::<$a, $lt, _>::new::<$protocol>(self as _)),)*
_ => ::core::option::Option::None
}
}
#[inline]
fn upcast_to_id_mut<$a>(
&$a mut self,
id: $crate::any::LtTypeId<$lt>
) -> ::core::option::Option<$crate::any::IndirectLtAny<$a, $lt, $crate::any::Mut>>
where
$lt: $a
{
match id {
$(id if id == $crate::any::LtTypeId::of::<$protocol>()
=> ::core::option::Option::Some($crate::any::IndirectLtAny::<$a, $lt, _>::new::<$protocol>(self as _)),)*
_ => ::core::option::Option::None
}
}
}
};
}
#[doc(inline)]
pub use any_trait;
#[must_use]
pub struct IndirectLtAny<'a, 'lt: 'a, I: Indirect<'a>> {
info: fn() -> (LtTypeId<'lt>, unsafe fn(RawIndirect)),
indirect: RawIndirect,
_marker: PhantomData<(I::ForT<fn(&'lt ()) -> &'lt ()>, PhantomPinned)>,
}
impl<'a, 'lt, I: Indirect<'a>> Drop for IndirectLtAny<'a, 'lt, I> {
fn drop(&mut self) {
// We need to drop the stored value.
// Lookup drop function.
let (_, drop_fn) = (self.info)();
// SAFETY: self.indirect is never touched again.
// Additionally, we know that drop_fn is for this self.indirect because it was
// made by Self::new.
unsafe { drop_fn(self.indirect) };
}
}
impl<'a, 'lt, I: Indirect<'a>> IndirectLtAny<'a, 'lt, I> {
/// Wrap an indirection.
///
/// The inner type `T` of the indirection is erased.
pub fn new<T: ?Sized + TypeNameable<'a, 'lt>>(indirect: I::ForT<T>) -> Self {
Self {
info: || {
(LtTypeId::of::<T>(), |raw| {
// SAFETY: This is only called in the drop impl.
unsafe { drop(I::from_raw::<T>(raw)) }
})
},
indirect: I::into_raw(indirect),
_marker: PhantomData,
}
}
/// Downcast to an indirection with a given `T` type.
///
/// If the type of the stored value is different, then `self` is
/// returned as is.
pub fn downcast<T: ?Sized + TypeNameable<'a, 'lt>>(self) -> Result<I::ForT<T>, Self> {
let (id, _) = (self.info)();
if id == LtTypeId::of::<T>() {
Ok(unsafe { I::from_raw::<T>(self.indirect) })
} else {
Err(self)
}
}
/// Type ID of the stored value's `T`.
pub fn id(&self) -> LtTypeId<'lt> {
(self.info)().0
}
}
pub unsafe trait Indirect<'a> {
type ForT<T: ?Sized + 'a>: 'a;
fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> RawIndirect;
unsafe fn from_raw<T: ?Sized + 'a>(any: RawIndirect) -> Self::ForT<T>;
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct RawIndirect(MaybeUninit<[u8; INDIRECT_SIZE]>);
const INDIRECT_SIZE: usize = core::mem::size_of::<usize>() * 2;
trait Helper {}
pub enum Ref {}
const _: () = assert!(core::mem::size_of::<&dyn Helper>() <= core::mem::size_of::<RawIndirect>());
unsafe impl<'a> Indirect<'a> for Ref {
type ForT<T: ?Sized + 'a> = &'a T;
fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> RawIndirect {
unsafe { transmute::<&'a T, RawIndirect>(value) }
}
unsafe fn from_raw<T: ?Sized + 'a>(any: RawIndirect) -> Self::ForT<T> {
unsafe { transmute::<RawIndirect, &'a T>(any) }
}
}
pub enum Mut {}
const _: () =
assert!(core::mem::size_of::<&mut dyn Helper>() <= core::mem::size_of::<RawIndirect>());
unsafe impl<'a> Indirect<'a> for Mut {
type ForT<T: ?Sized + 'a> = &'a mut T;
fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> RawIndirect {
unsafe { transmute::<&'a mut T, RawIndirect>(value) }
}
unsafe fn from_raw<T: ?Sized + 'a>(any: RawIndirect) -> Self::ForT<T> {
unsafe { transmute::<RawIndirect, &'a mut T>(any) }
}
}
#[cfg(feature = "alloc")]
pub use boxed::*;
#[cfg(feature = "alloc")]
mod boxed {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
/// A `Box<T>` indirection.
pub enum Boxed {}
const _: () =
assert!(core::mem::size_of::<Box<dyn Helper>>() <= core::mem::size_of::<RawIndirect>());
unsafe impl<'a> Indirect<'a> for Boxed {
type ForT<T: ?Sized + 'a> = Box<T>;
fn into_raw<T: ?Sized + 'a>(value: Box<T>) -> RawIndirect {
unsafe { transmute::<Box<T>, RawIndirect>(value) }
}
unsafe fn from_raw<T: ?Sized + 'a>(any: RawIndirect) -> Box<T> {
unsafe { transmute::<RawIndirect, Box<T>>(any) }
}
}
}
/// # Safety
/// Same rules as [`core::mem::transmute()`].
unsafe fn transmute<T, U>(value: T) -> U {
// Create union type that can store a `T` or a `U`.
// We can then use this to convert between them.
//
// The repr(C) layout forces no offset between `t` and `u` as talked about here
// https://rust-lang.github.io/unsafe-code-guidelines/layout/unions.html#c-compatible-layout-repr-c
#[repr(C)]
union Transmute<T, U> {
t: ManuallyDrop<T>,
u: ManuallyDrop<U>,
}
// Create the union in the `T` state.
let value = Transmute {
t: ManuallyDrop::new(value),
};
// Read from the union in the `U` state.
// SAFETY: This is safe because the caller has promised that `T` can be transmuted to `U`.
// The following reference link talks about repr(C) unions being used this way.
// https://doc.rust-lang.org/reference/items/unions.html#reading-and-writing-union-fields
ManuallyDrop::into_inner(unsafe { value.u })
}
#[cfg(test)]
mod test {
use super::*;
#[derive(Debug, PartialEq)]
struct X<'a>(&'a mut i32);
nameable! {
['a, 'lt]
X<'lt> where {'lt: 'a}
X<'lt> where {'lt: 'a}
}
#[test]
fn implementer_macro() {
trait Z {}
nameable! {
['a, 'ctx]
dyn Z + 'a where {'ctx: 'a}
}
struct X<T>(T);
impl<T: Clone> Z for X<T> {}
any_trait! {
impl['a, 'ctx, T: Clone] X<T> = [
dyn Z + 'a
];
}
}
#[test]
fn any_trait_macro() {
trait Z {
fn num(&self) -> i32;
}
nameable! {
['a, 'ctx]
dyn Z + 'a where {'ctx: 'a}
}
struct X(i32);
impl Z for X {
fn num(&self) -> i32 {
self.0
}
}
any_trait! {
impl['a, 'ctx] X = [
dyn Z + 'a
];
}
let x = X(42);
let y: &dyn AnyTrait = &x;
let z: &dyn Z = y.upcast().unwrap();
assert_eq!(z.num(), 42);
}
}