issue with lt_any
| -rw-r--r-- | src/any.rs | 252 | ||||
| -rw-r--r-- | src/protocol.rs | 150 |
2 files changed, 94 insertions, 308 deletions
@@ -2,9 +2,9 @@ //! //! [`Any`] is generic over the type of indirection (`&T`, `&mut T`, `Box<T>`) //! and allows erasing the `T` of the indirection. This is similar to replacing the `T` with -//! [`core::any::Any`]. The main difference to [`core::any::Any`] is +//! [`core::any::Any`]. The main difference to [`core::any::Any`] is -use core::{any::TypeId, marker::{PhantomData, PhantomPinned}, mem::{MaybeUninit, ManuallyDrop}}; +use core::marker::PhantomData; pub trait TypeNameable<'lt> { type Name: TypeName<'lt, Nameable = Self>; @@ -14,183 +14,80 @@ pub trait TypeName<'lt>: 'static { type Nameable: ?Sized + TypeNameable<'lt, Name = Self>; } -const INDIRECT_SIZE: usize = core::mem::size_of::<usize>() * 2; - -#[derive(Clone, Copy)] -#[repr(transparent)] -pub struct RawIndirectAny(MaybeUninit<[u8; INDIRECT_SIZE]>); - -pub unsafe trait Indirect<'a> { - type ForT<T: ?Sized + 'a>: 'a; - - fn into_any<T: ?Sized + 'a>(value: Self::ForT<T>) -> RawIndirectAny; - - unsafe fn from_any<T: ?Sized + 'a>(any: RawIndirectAny) -> Self::ForT<T>; -} - -trait Helper {} - -/// A `&T` indirection. -pub enum Ref {} - -const _: () = assert!(core::mem::size_of::<&dyn Helper>() <= core::mem::size_of::<RawIndirectAny>()); - -unsafe impl<'a> Indirect<'a> for Ref { - type ForT<T: ?Sized + 'a> = &'a T; - - fn into_any<T: ?Sized + 'a>(value: &'a T) -> RawIndirectAny { - unsafe { transmute::<&'a T, RawIndirectAny>(value) } - } - - unsafe fn from_any<T: ?Sized + 'a>(any: RawIndirectAny) -> &'a T { - unsafe { transmute::<RawIndirectAny, &'a T>(any) } - } -} - -/// A `&mut T` indirection. -pub enum Mut {} - -const _: () = assert!(core::mem::size_of::<&mut dyn Helper>() <= core::mem::size_of::<RawIndirectAny>()); - -unsafe impl<'a> Indirect<'a> for Mut { - type ForT<T: ?Sized + 'a> = &'a mut T; - - fn into_any<T: ?Sized + 'a>(value: &'a mut T) -> RawIndirectAny { - unsafe { transmute::<&'a mut T, RawIndirectAny>(value) } - } - - unsafe fn from_any<T: ?Sized + 'a>(any: RawIndirectAny) -> &'a mut T { - unsafe { transmute::<RawIndirectAny, &'a mut T>(any) } - } +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)] +pub struct LtTypeId<'lt> { + _marker: PhantomData<fn(&'lt ()) -> &'lt ()>, + name_id: core::any::TypeId, } -#[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::<RawIndirectAny>()); - - unsafe impl<'a> Indirect<'a> for Boxed { - type ForT<T: ?Sized + 'a> = Box<T>; - - fn into_any<T: ?Sized + 'a>(value: Box<T>) -> RawIndirectAny { - unsafe { transmute::<Box<T>, RawIndirectAny>(value) } - } - - unsafe fn from_any<T: ?Sized + 'a>(any: RawIndirectAny) -> Box<T> { - unsafe { transmute::<RawIndirectAny, Box<T>>(any) } +impl<'lt> LtTypeId<'lt> { + pub fn of<T: ?Sized + TypeNameable<'lt>>() -> Self { + LtTypeId { + _marker: PhantomData, + name_id: core::any::TypeId::of::<T::Name>(), } } } -/// Container for (almost) any indirection. -/// -/// An indirection is something like a `&T` that is a pointer. -/// The `'a` lifetime is the lifetime of the indirection, and `'lt` -/// is a lifetime the stored value's `T` can contain. -/// -/// This type is designed to allow using a generic containing type with -/// a trait object method. While the value is stored in the [`Any`] it cannot -/// be accessed. The [`Any`] must be downcasted with [`Any::downcast()`] to access -/// the value. -#[must_use] -pub struct Any<'a, 'lt: 'a, I: Indirect<'a>> { - /// The meta information about the value is stored as a function pointer - /// to reduce it's size to one pointer. - info: fn() -> (TypeId, unsafe fn(RawIndirectAny)), - - /// The indirect pointer. - indirect: RawIndirectAny, - - /// Invariant over `'lt` and holding a `I::ForT`. - _marker: PhantomData<(I::ForT<()>, *mut &'lt (), PhantomPinned)>, +pub unsafe trait LtAny<'lt> { + fn type_id(&self) -> LtTypeId<'lt>; } -impl<'a, 'lt, I: Indirect<'a>> Drop for Any<'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) }; +unsafe impl<'lt, T: ?Sized + TypeNameable<'lt>> LtAny<'lt> for T { + fn type_id(&self) -> LtTypeId<'lt> { + LtTypeId::of::<T>() } } -impl<'a, 'lt, I: Indirect<'a>> Any<'a, 'lt, I> { - /// Wrap an indirection. - /// - /// The inner type `T` of the indirection is erased. - pub fn new<T: TypeNameable<'lt>>(indirect: I::ForT<T>) -> Self { - Self { - info: || ( - TypeId::of::<T::Name>(), - |raw| { - // SAFETY: This is only called in the drop impl. - unsafe { drop(I::from_any::<T>(raw)) - }} - ), - indirect: I::into_any(indirect), - _marker: PhantomData, +impl<'a, 'lt> dyn LtAny<'lt> + 'a { + pub fn is<T: ?Sized + TypeNameable<'lt>>(&self) -> bool { + LtTypeId::of::<T>() == self.type_id() + } + + pub fn downcast_ref<T: ?Sized + TypeNameable<'lt>>(&self) -> Option<&T> { + if self.is::<T>() { + Some(unsafe { &*(self as *const dyn LtAny<'lt> as *const T) }) + } else { + None } } - /// 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: TypeNameable<'lt>>(self) -> Result<I::ForT<T>, Self> { - let (id, _) = (self.info)(); - - if id == TypeId::of::<T::Name>() { - Ok(unsafe { I::from_any(self.indirect) }) + pub fn downcast_mut<T: ?Sized + TypeNameable<'lt>>(&mut self) -> Option<&mut T> { + if self.is::<T>() { + Some(unsafe { &mut *(self as *mut dyn LtAny<'lt> as *mut T) }) } else { - Err(self) + None } } - /// Type ID of the stored value's `T`. - pub fn id(&self) -> TypeId { - (self.info)().0 + #[cfg(feature = "alloc")] + pub fn downcast_box<T: TypeNameable<'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) + } } } -/// # 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), - }; +#[doc(hidden)] +#[macro_export] +macro_rules! nameable { + {$lt:lifetime: $type:ty => $name:ty} => { + impl<$lt> $crate::any::TypeNameable<$lt> for $type { + type Name = $name; + } - // 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 }) + impl<$lt> TypeName<$lt> for $name { + type Nameable = $type; + } + } } +#[doc(inline)] +pub use nameable; #[cfg(test)] mod test { @@ -199,37 +96,50 @@ mod test { #[derive(Debug, PartialEq)] struct X<'a>(&'a mut i32); - enum Y {} - - impl<'a> TypeNameable<'a> for X<'a> { - type Name = Y; - } - - impl<'a> TypeName<'a> for Y { - type Nameable = X<'a>; - } + nameable!('a: X<'a> => X<'static>); #[test] fn any() { let mut x = 42; let x = X(&mut x); - let any = Any::<Ref>::new(&x); + let any: &dyn LtAny = &x; - let Ok(y) = any.downcast() else { panic!() }; + let y = any.downcast_ref().unwrap(); assert_eq!(x, *y); } #[test] #[cfg(feature = "alloc")] - fn any_box_drop() { - #[cfg(not(feature = "std"))] - use alloc::boxed::Box; + fn any_box() { + let mut x = 42; + let x = X(&mut x); + + let any: Box<dyn LtAny> = Box::new(x); + let Ok(y) = any.downcast_box::<X>() else { panic!() }; + + assert_eq!(*y.0, 42); + } + + #[test] + fn equal() { let mut x = 42; let x = X(&mut x); - let _ = Any::<Boxed>::new(Box::new(x)); + let any: &dyn LtAny = &x; + + { + let mut x2 = 42; + let x2 = X(&mut x2); + + let any2: &dyn LtAny = &x2; + + assert_eq!(any.type_id(), any2.type_id()); + } + + // Forcing any to live until here will make it fail. + // drop(any); } } diff --git a/src/protocol.rs b/src/protocol.rs index e201c44..8511c46 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -33,13 +33,6 @@ //! This is done via the help of the [`AnyImpl`] type. This is not required for the core //! idea of DIDETs. -mod any; -mod id; - -pub use id::ProtocolId; - -use any_object::{AnyObject, Mut, Ref}; - /// Type nameable trait. /// /// Traits cannot by named in the type system. Trait objects can. However, @@ -68,7 +61,7 @@ use any_object::{AnyObject, Mut, Ref}; /// type Object<'a, 'ctx: 'a> = dyn MyTrait<'ctx> + 'a; /// } /// ``` -pub trait Protocol: 'static { +pub trait Protocol<'ctx>: TypeNameable<'ctx> { /// The trait object form of the trait. /// /// This should be of the form `dyn Trait<'ctx> + 'a` where `'a` sets the @@ -76,50 +69,38 @@ pub trait Protocol: 'static { /// /// Note, it is possible (and safe) to put non-trait object type here, but /// it is likely to not play well with [`AnyObject`]. - type Object<'a, 'ctx: 'a>: ?Sized; -} - -/// Extension trait for getting the ID of a protocol. -pub trait ProtocolExt: Protocol { - /// Get the protocol's ID. - fn id() -> ProtocolId; -} - -impl<T: Protocol> ProtocolExt for T { - fn id() -> ProtocolId { - ProtocolId::of::<T>() - } + type Object<'a>: ?Sized where 'ctx: 'a; } pub trait Implementer<'ctx> { - fn interface(&self, id: ProtocolId) -> Option<AnyObject<'_, 'ctx, Ref>>; + fn interface(&self, id: LtTypeId<'ctx>) -> Option<&dyn LtAny<'ctx>>; } pub trait ImplementerMut<'ctx> { - fn interface(&mut self, id: ProtocolId) -> Option<AnyObject<'_, 'ctx, Mut>>; + fn interface_mut(&mut self, id: LtTypeId<'ctx>) -> Option<&mut dyn LtAny<'ctx>>; } /// Extension trait for getting the implementation of a protocol. -pub trait ImplementerExt<'ctx>: Implementer<'ctx> { +pub trait ImplementerMutExt<'ctx>: ImplementerMut<'ctx> { /// Get an implementation given a protocol type. /// /// This wraps [`Implementer::interface`] and [`AnyImpl::downcast`]. /// If [`Implementer::interface`] returns a [`AnyImpl`] for the wrong protocol then a panic is /// generated. - fn interface_for<'a, P: Protocol>(&'a mut self) -> Option<&'a mut P::Object<'a, 'ctx>> + fn interface_mut_for<'a, P: Protocol<'ctx>>(&'a mut self) -> Option<&'a mut P::Object<'a>> where 'ctx: 'a; } -impl<'ctx, T: Implementer<'ctx> + ?Sized> ImplementerExt<'ctx> for T { - fn interface_for<'a, P: Protocol>(&'a mut self) -> Option<&'a mut P::Object<'a, 'ctx>> +impl<'ctx, T: ImplementerMut<'ctx> + ?Sized> ImplementerMutExt<'ctx> for T { + fn interface_mut_for<'a, P: Protocol<'ctx>>(&'a mut self) -> Option<&'a mut P::Object<'a>> where 'ctx: 'a, { - match self.interface(P::id()) { - Some(interface) => match interface.downcast::<P>() { - Ok(implementation) => Some(implementation), - Err(interface) => panic!( + match self.interface_mut(LtTypeId::of::<P>()) { + Some(interface) => match interface.downcast_mut::<P::Object<'a>>() { + Some(implementation) => Some(implementation), + None => panic!( "unexpected protocol implementation: `{:?}`, expected: `{:?}`", interface.id(), P::id() @@ -155,112 +136,7 @@ macro_rules! implementer { #[doc(inline)] pub use implementer; -pub mod any_object { - use core::{marker::PhantomData, mem::MaybeUninit}; - - use super::{Protocol, ProtocolExt, ProtocolId}; - - trait Helper<'ctx> {} - - const INDIRECT_SIZE: usize = core::mem::size_of::<usize>() * 2; - - pub trait Indirect<'a> { - type WithT<T: ?Sized + 'a>: 'a; - - fn into_any<T: ?Sized + 'a>(value: Self::WithT<T>) -> MaybeUninit<[u8; INDIRECT_SIZE]>; - - unsafe fn from_any<T: ?Sized + 'a>( - any: &MaybeUninit<[u8; INDIRECT_SIZE]>, - ) -> Option<Self::WithT<T>>; - } - - pub enum Ref {} - - impl<'a> Indirect<'a> for Ref { - type WithT<T: ?Sized + 'a> = &'a T; - - fn into_any<T: ?Sized + 'a>(value: &'a T) -> MaybeUninit<[u8; INDIRECT_SIZE]> { - const _: () = { - assert!(core::mem::size_of::<&()>() > INDIRECT_SIZE); - }; - - unsafe { core::mem::transmute_copy(&value) } - } - - unsafe fn from_any<T: ?Sized + 'a>( - any: &MaybeUninit<[u8; INDIRECT_SIZE]>, - ) -> Option<Self::WithT<T>> { - todo!() - } - } - - pub enum Mut {} - - impl<'a> Indirect<'a> for Mut { - type WithT<T: ?Sized + 'a> = &'a mut T; - - fn into_any<T: ?Sized + 'a>(value: Self::WithT<T>) -> MaybeUninit<[u8; INDIRECT_SIZE]> { - todo!() - } - - unsafe fn from_any<T: ?Sized + 'a>( - any: &MaybeUninit<[u8; INDIRECT_SIZE]>, - ) -> Option<Self::WithT<T>> { - todo!() - } - } - - /// A [`Implementation`] for any `P`. - /// - /// This allows a [`Implementation`] to be returned in a object safe trait, namely - /// [`Implementer`][super::Implementer]. - pub struct AnyObject<'a, 'ctx: 'a, I: Indirect<'a>> { - /// ID of the protocol the ptr is for. - id: ProtocolId, - - /// A trait object pointer stored in raw form. - indirect: MaybeUninit<[u8; INDIRECT_SIZE]>, - - /// A marker for what `fat_ptr` is storing. - _marker: PhantomData<I::WithT<dyn Helper<'ctx> + 'a>>, - } - - impl<'a, 'ctx, I: Indirect<'a>> AnyObject<'a, 'ctx, I> { - /// Wrap a [`Implementation`] trait object to erase it's `P` type. - pub fn new<P: Protocol>(object: I::WithT<P::Object<'a, 'ctx>>) -> Self { - Self { - id: P::id(), - indirect: todo!(), - _marker: PhantomData, - } - } - - /// Downcast to a [`Implementation`] trait object with a given `P` type. - /// - /// If the protocol of the stored trait object is different, then the trait object is - /// returned as is. - pub fn downcast<P: Protocol>(self) -> Result<&'a mut P::Object<'a, 'ctx>, Self> { - if self.id == P::id() { - // SAFETY: Only `new` can make a value of this type, and it stores the ID of `P`. - // If the IDs are equal then we can act like any and downcast back to the real - // type. - // - // An important note is this method takes ownership. Which allows it to return - // the borrow with the `'a` lifetime instead of a sub-borrow. - let object: *mut P::Object<'a, 'ctx> = - unsafe { core::mem::transmute_copy(&self.fat_ptr) }; - Ok(unsafe { &mut *object }) - } else { - Err(self) - } - } - - /// ID of the protocol this [`Implementation`] is for. - pub fn id(&self) -> ProtocolId { - self.id - } - } -} +use crate::any::{LtTypeId, LtAny, TypeNameable}; #[cfg(test)] mod test { |