new lifetime containing type ids are working
| -rw-r--r-- | src/any.rs | 290 | ||||
| -rw-r--r-- | src/builtins.rs | 2 | ||||
| -rw-r--r-- | src/builtins/walker/hint.rs | 101 | ||||
| -rw-r--r-- | src/lib.rs | 8 | ||||
| -rw-r--r-- | src/protocol.rs | 103 |
5 files changed, 353 insertions, 151 deletions
@@ -1,19 +1,85 @@ -//! Heapless type erasure. -//! -//! [`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 +use core::{ + marker::{PhantomData, PhantomPinned}, + mem::{ManuallyDrop, MaybeUninit}, +}; -use core::marker::PhantomData; +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)*} -pub trait TypeNameable<'lt> { - type Name: TypeName<'lt, Nameable = Self>; + $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 trait TypeName<'lt>: 'static { - type Nameable: ?Sized + TypeNameable<'lt, Name = Self>; +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 ()>, @@ -21,7 +87,7 @@ pub struct LtTypeId<'lt> { } impl<'lt> LtTypeId<'lt> { - pub fn of<T: ?Sized + TypeNameable<'lt>>() -> Self { + pub fn of<'a, T: ?Sized + TypeNameable<'a, 'lt>>() -> Self { LtTypeId { _marker: PhantomData, name_id: core::any::TypeId::of::<T::Name>(), @@ -33,18 +99,18 @@ pub unsafe trait LtAny<'lt> { fn type_id(&self) -> LtTypeId<'lt>; } -unsafe impl<'lt, T: ?Sized + TypeNameable<'lt>> LtAny<'lt> for T { +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<'lt>>(&self) -> bool { + pub fn is<T: ?Sized + TypeNameable<'a, 'lt>>(&self) -> bool { LtTypeId::of::<T>() == self.type_id() } - - pub fn downcast_ref<T: ?Sized + TypeNameable<'lt>>(&self) -> Option<&T> { + + 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 { @@ -52,7 +118,7 @@ impl<'a, 'lt> dyn LtAny<'lt> + 'a { } } - pub fn downcast_mut<T: ?Sized + TypeNameable<'lt>>(&mut self) -> Option<&mut T> { + 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 { @@ -61,7 +127,7 @@ impl<'a, 'lt> dyn LtAny<'lt> + 'a { } #[cfg(feature = "alloc")] - pub fn downcast_box<T: TypeNameable<'lt>>(self: Box<Self>) -> Result<Box<T>, Box<Self>> { + 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); @@ -73,73 +139,177 @@ impl<'a, 'lt> dyn LtAny<'lt> + 'a { } } -#[doc(hidden)] -#[macro_export] -macro_rules! nameable { - {$lt:lifetime: $type:ty => $name:ty} => { - impl<$lt> $crate::any::TypeNameable<$lt> for $type { - type Name = $name; +#[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)(); - impl<$lt> TypeName<$lt> for $name { - type Nameable = $type; + 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 + } } -#[doc(inline)] -pub use nameable; -#[cfg(test)] -mod test { - use super::*; +pub unsafe trait Indirect<'a> { + type ForT<T: ?Sized + 'a>: 'a; - #[derive(Debug, PartialEq)] - struct X<'a>(&'a mut i32); + fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> RawIndirect; + + unsafe fn from_raw<T: ?Sized + 'a>(any: RawIndirect) -> Self::ForT<T>; +} - nameable!('a: X<'a> => X<'static>); +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct RawIndirect(MaybeUninit<[u8; INDIRECT_SIZE]>); - #[test] - fn any() { - let mut x = 42; - let x = X(&mut x); +const INDIRECT_SIZE: usize = core::mem::size_of::<usize>() * 2; - let any: &dyn LtAny = &x; +trait Helper {} - let y = any.downcast_ref().unwrap(); +pub enum Ref {} - assert_eq!(x, *y); +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) } } - #[test] - #[cfg(feature = "alloc")] - fn any_box() { - let mut x = 42; - let x = X(&mut x); + unsafe fn from_raw<T: ?Sized + 'a>(any: RawIndirect) -> Self::ForT<T> { + unsafe { transmute::<RawIndirect, &'a T>(any) } + } +} - let any: Box<dyn LtAny> = Box::new(x); +pub enum Mut {} - let Ok(y) = any.downcast_box::<X>() else { panic!() }; +const _: () = + assert!(core::mem::size_of::<&mut dyn Helper>() <= core::mem::size_of::<RawIndirect>()); - assert_eq!(*y.0, 42); +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) } } +} - #[test] - fn equal() { - let mut x = 42; - let x = X(&mut x); +#[cfg(feature = "alloc")] +pub use boxed::*; +#[cfg(feature = "alloc")] +mod boxed { + use super::*; + + #[cfg(not(feature = "std"))] + use alloc::boxed::Box; - let any: &dyn LtAny = &x; + /// A `Box<T>` indirection. + pub enum Boxed {} - { - let mut x2 = 42; - let x2 = X(&mut x2); + const _: () = + assert!(core::mem::size_of::<Box<dyn Helper>>() <= core::mem::size_of::<RawIndirect>()); - let any2: &dyn LtAny = &x2; + unsafe impl<'a> Indirect<'a> for Boxed { + type ForT<T: ?Sized + 'a> = Box<T>; - assert_eq!(any.type_id(), any2.type_id()); + fn into_raw<T: ?Sized + 'a>(value: Box<T>) -> RawIndirect { + unsafe { transmute::<Box<T>, RawIndirect>(value) } } - // Forcing any to live until here will make it fail. - // drop(any); + 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} } } diff --git a/src/builtins.rs b/src/builtins.rs index 3a5e50e..823135f 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -1,2 +1,2 @@ -pub mod visitor; +// pub mod visitor; pub mod walker; diff --git a/src/builtins/walker/hint.rs b/src/builtins/walker/hint.rs index 42234e9..4392f73 100644 --- a/src/builtins/walker/hint.rs +++ b/src/builtins/walker/hint.rs @@ -4,42 +4,109 @@ //! this module gives a protocol by which a visitor can give a hint //! to the walker about what it is expecting. -use crate::protocol::{Implementer, Protocol}; +use core::marker::PhantomData; -/// Protocol for giving a hint to a walker. -/// -/// A hint is for a particular protocol `P`. -pub struct Hint<P: Meta>(P); +use crate::{ + any::{TypeName, TypeNameable}, + nameable, + protocol::Implementer, +}; /// Meta information for the hint. /// /// This gives the visitor more information to work from when selecting a hint. -pub trait Meta: Protocol { +pub trait Meta<'ctx> { /// Information known by the walker. /// /// This should be information easy to get without changing the state of the walker /// in an irreversable way. - type Known<'a, 'ctx: 'a>; + type Known<'a>; /// Extra information the visitor can give to the walker about what it is expecting. - type Hint<'a, 'ctx: 'a>; + type Hint<'a>; } /// Object implementing the [`Hint`] protocol. -pub trait Object<'ctx, P: Meta> { +pub trait Hint<'ctx, P: Meta<'ctx>> { /// Hint to the walker to use the `P` protocol. /// /// This should only be called once per [`RequestHint`]. - fn hint( - &mut self, - visitor: &mut dyn Implementer<'ctx>, - hint: P::Hint<'_, 'ctx>, - ) -> Result<(), ()>; + fn hint(&mut self, visitor: &mut dyn Implementer<'ctx>, hint: P::Hint<'_>) -> Result<(), ()>; /// Ask the walker for information about it's support of the protocol. - fn known(&mut self, hint: &P::Hint<'_, 'ctx>) -> Result<P::Known<'_, 'ctx>, ()>; + fn known(&mut self, hint: &P::Hint<'_>) -> Result<P::Known<'_>, ()>; } -impl<P: Meta> Protocol for Hint<P> { - type Object<'a, 'ctx: 'a> = &'a mut dyn Object<'ctx, P>; +// nameable!(['a, 'ctx, P: Meta<'ctx> + TypeNameable<'a, 'ctx>]: dyn Hint<'ctx, P> + 'a => dyn Hint<'static, P::Name>); + +const _: () = { + impl<'a, 'ctx: 'a, P: TypeNameable<'a, 'ctx>> + crate::any::TypeNameable<'a, 'ctx> for dyn Hint<'ctx, P> + 'a + where + P: ?Sized + // where + // P: Meta<'ctx> + { + type Name = Name<P::Name>; + } + + pub struct Name<T: ?Sized>(PhantomData<fn() -> *const T>); + + impl<'a, 'ctx: 'a, T: TypeName<'a, 'ctx>> crate::any::TypeName<'a, 'ctx> for Name<T> + where + T: ?Sized + // where + // T::Nameable: Meta<'ctx>, + { + type Nameable = dyn Hint<'ctx, T::Nameable> + 'a; + } +}; + +#[cfg(test)] +mod test { + use crate::any::LtTypeId; + + use super::*; + + #[test] + fn demo() { + struct X; + struct Y; + + nameable!(['a, 'ctx]: Y => Y); + + impl<'ctx, X> Hint<'ctx, Y> for X { + fn hint( + &mut self, + visitor: &mut dyn Implementer<'ctx>, + hint: <Y as Meta>::Hint<'_>, + ) -> Result<(), ()> { + todo!() + } + + fn known( + &mut self, + hint: &<Y as Meta>::Hint<'_>, + ) -> Result<<Y as Meta>::Known<'_>, ()> { + todo!() + } + } + + impl<'ctx> Meta<'ctx> for Y { + type Known<'a> = (); + + type Hint<'a> = (); + } + + let x = X; + let y: &dyn Hint<Y> = &x; + + fn id<'a, 'ctx, T: ?Sized + TypeNameable<'a, 'ctx>>(x: &T) { + dbg!(LtTypeId::of::<T>()); + } + + id(y); + + todo!(); + } } @@ -6,12 +6,12 @@ #[cfg(feature = "alloc")] extern crate alloc; -// mod build; -// pub mod builtins; -// pub mod protocol; pub mod any; +mod build; +// pub mod builtins; +pub mod protocol; pub mod symbol; -// mod walk; +mod walk; // pub mod impls; // pub mod transform; diff --git a/src/protocol.rs b/src/protocol.rs index 8511c46..0c20eec 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -33,51 +33,14 @@ //! This is done via the help of the [`AnyImpl`] type. This is not required for the core //! idea of DIDETs. -/// Type nameable trait. -/// -/// Traits cannot by named in the type system. Trait objects can. However, -/// trait objects are not always `'static` so don't always have a -/// [`TypeId`][core::any::TypeId]. This trait provides a way to name a trait object -/// in the type system and with a `TypeId` even if it contains lifetimes. -/// -/// [`Protocol`] should be implemented on a marker type that is `'static`. -/// [`Protocol`] then gives a mapping from that marker type to a trait object -/// given by [`Protocol::Object`]. Its recommended to use uninhabited marker -/// types when possible as the marker never needs to exist as a value. -/// -/// The `'ctx` lifetime is a lifetime the trait object can contain. The `'a` lifetime -/// is the lifetime of a reference to the trait object. As such, the trait object -/// needs to live for at least `'a`. -/// -/// ``` -/// // Some trait we want to use as a protocol. -/// trait MyTrait<'ctx> {} -/// -/// // Type to name MyTrait in the type system. -/// enum MyTraitProtocol {} -/// -/// // By implementing this we map MyTraitProtocol to MyTrait. -/// impl Protocol for MyTraitProtocol { -/// type Object<'a, 'ctx: 'a> = dyn MyTrait<'ctx> + 'a; -/// } -/// ``` -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 - /// required lifetime of the trait object. - /// - /// 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>: ?Sized where 'ctx: 'a; -} +use crate::any::{IndirectLtAny, LtAny, LtTypeId, Mut, Ref, TypeNameable}; pub trait Implementer<'ctx> { - fn interface(&self, id: LtTypeId<'ctx>) -> Option<&dyn LtAny<'ctx>>; + fn interface(&self, id: LtTypeId<'ctx>) -> Option<IndirectLtAny<'_, 'ctx, Ref>>; } pub trait ImplementerMut<'ctx> { - fn interface_mut(&mut self, id: LtTypeId<'ctx>) -> Option<&mut dyn LtAny<'ctx>>; + fn interface_mut<'a>(&'a mut self, id: LtTypeId<'ctx>) -> Option<IndirectLtAny<'a, 'ctx, Mut>> where 'ctx: 'a; } /// Extension trait for getting the implementation of a protocol. @@ -87,23 +50,23 @@ pub trait ImplementerMutExt<'ctx>: ImplementerMut<'ctx> { /// This wraps [`Implementer::interface`] and [`AnyImpl::downcast`]. /// If [`Implementer::interface`] returns a [`AnyImpl`] for the wrong protocol then a panic is /// generated. - fn interface_mut_for<'a, P: Protocol<'ctx>>(&'a mut self) -> Option<&'a mut P::Object<'a>> - where + fn interface_mut_for<'a, P: TypeNameable<'a, 'ctx>>(&'a mut self) -> Option<&'a mut P> + where 'ctx: 'a; } 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, + fn interface_mut_for<'a, P: TypeNameable<'a, 'ctx>>(&'a mut self) -> Option<&'a mut P> + where + 'ctx: 'a { match self.interface_mut(LtTypeId::of::<P>()) { - Some(interface) => match interface.downcast_mut::<P::Object<'a>>() { - Some(implementation) => Some(implementation), - None => panic!( + Some(interface) => match interface.downcast::<P>() { + Ok(implementation) => Some(implementation), + Err(interface) => panic!( "unexpected protocol implementation: `{:?}`, expected: `{:?}`", interface.id(), - P::id() + LtTypeId::of::<P>() ), }, None => None, @@ -116,17 +79,20 @@ impl<'ctx, T: ImplementerMut<'ctx> + ?Sized> ImplementerMutExt<'ctx> for T { #[macro_export] macro_rules! implementer { { - impl[$ctx:lifetime $($generic:tt)*] $name:ty = [$($protocol:ty),* $(,)?]; + impl[$a:lifetime, $ctx:lifetime $($generic:tt)*] $name:ty = [$($protocol:ty),* $(,)?]; } => { - impl<$ctx $($generic)*> $crate::protocol::Implementer<$ctx> for $name { + impl<$ctx $($generic)*> $crate::protocol::ImplementerMut<$ctx> for $name { #[inline] - fn interface( - &mut self, - id: $crate::protocol::ProtocolId - ) -> ::core::option::Option<$crate::protocol::AnyObject<'_, $ctx>> { + fn interface_mut<$a>( + &$a mut self, + id: $crate::any::LtTypeId<$ctx> + ) -> ::core::option::Option<$crate::any::IndirectLtAny<$a, $ctx, $crate::any::Mut>> + where + $ctx: $a + { match id { - $(id if id == $crate::protocol::ProtocolId::of::<$protocol>() - => Some($crate::protocol::AnyObject::new::<$protocol>(self)),)* + $(id if id == $crate::any::LtTypeId::of::<$protocol>() + => Some($crate::any::IndirectLtAny::<$a, $ctx, _>::new::<$protocol>(self as _)),)* _ => None } } @@ -136,30 +102,29 @@ macro_rules! implementer { #[doc(inline)] pub use implementer; -use crate::any::{LtTypeId, LtAny, TypeNameable}; - #[cfg(test)] mod test { + use crate::nameable; + use super::*; #[test] fn implementer_macro() { - struct X<T>(T); - - enum Y {} - trait Z {} - impl Protocol for Y { - type Object<'a, 'ctx> = dyn Z + 'a; + nameable! { + ['a, 'ctx] + dyn Z + 'a where {'ctx: 'a} } + struct X<T>(T); + + impl<T: Clone> Z for X<T> {} + implementer! { - impl['ctx, T: Clone] X<T> = [ - Y + impl['a, 'ctx, T: Clone] X<T> = [ + dyn Z + 'a ]; } - - impl<T: Clone> Z for X<T> {} } } |