Diffstat (limited to 'src/any.rs')
| -rw-r--r-- | src/any.rs | 585 |
1 files changed, 222 insertions, 363 deletions
@@ -1,48 +1,52 @@ //! Extended type erasure support. //! -//! The [`core::any`] module, and [`Any`][core::any::Any] in particular, -//! provide great utilities to perform type erasure. However, there are some -//! limitations. Namely `Any` requires the type be `'static`. This doesn't -//! work for [`treaty`][crate] as it needs the `'ctx` lifetime. -//! Also `Any` doesn't allow type erasing `!Sized` types as this would -//! result in a "double fat pointer"?!, and those don't exist. -//! -//! This module solves both problems. First, [`LtAny`] is a lifetime containing -//! counterpart to [`Any`][core::any::Any]. [`LtAny`] allows one lifetime which -//! [`treaty`][crate] uses for the `'ctx` lifetime shared by walkers and visitors. -//! Second, [`IndirectLtAny`] is able to type erase a pointer like type even -//! if it's a `!Sized` type. This allows for type erasing borrows of trait objects -//! like `&dyn Trait`. -//! -//! For a type to be compatible with this module it needs to implement [`TypeNameable`] -//! to give it a unique [`TypeId`][core::any::TypeId]. This can be done manually -//! without unsafe code. However, its recommended to use the provided [`nameable`] -//! macro when possible. +//! The `AnyTrait` trait provides dynamic upcasting to trait objects. +pub mod indirect; pub mod static_wrapper; +mod type_name_id; use crate::{bijective_higher_ranked_trait, bijective_higher_ranked_type}; -use core::{ - any::TypeId, - marker::{PhantomData, PhantomPinned}, - mem::{ManuallyDrop, MaybeUninit}, -}; +use core::marker::PhantomData; + +pub use type_name_id::*; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::boxed::Box; bijective_higher_ranked_trait! { - pub type class MaybeSized['lt][]: [for<'a>] + /// Higher ranked types with a context `'ctx` lifetime. + /// + /// Its recommended to name types of this class with a `Dyn` prefix. + /// This prefix was chosen because most lower types used in treaty are trait objects. + /// + /// This is most important for trait objects. For example `dyn MyTrait<'ctx> + 'a` this + /// type has a "extra" `'a` lifetime for how long its valid for. This higher ranked type class + /// allows naming the trait object type without a `'a` being given. + pub type class WithContextLt['ctx][]: {} [for<'a>] } bijective_higher_ranked_trait! { - pub type class TypeName[][]: {'static} [for<'lt> MaybeSized::Trait<'lt> + Send] + /// Higher ranked types that are `'static`. + /// + /// Types of this class can usually be sealed as they don't need to be named directly. + /// + /// Higher ranked types of this form have a [`TypeId`] associated with them. + /// This allows them to be used as a name for lifetime containing types. + /// + /// This type class has members in the [`WithContextLt`] higher ranked type class. + /// To get a concrete type two lowerings need to be applied to inject two lifetimes. + /// One for the context lifetime, and one for the lifetime of the concrete type. + pub type class TypeName[][]: {'static} [for<'ctx> WithContextLt::MemberType<'ctx> + Send] } bijective_higher_ranked_type! { - pub type DynRef['lt][][T['lt][]]: MaybeSized['lt][] + /// Higher ranked type for borrows `&T`. + /// + /// The borrow gets the `'a` lifetime, not the `'ctx` lifetime. + pub type DynRef['ctx][][T['ctx][]]: WithContextLt['ctx][] for<'a> - (&'a MaybeSized::T<'a, 'lt, T>) + (&'a WithContextLt::T<'a, 'ctx, T>) (&'a T) where { T: ?Sized @@ -51,18 +55,21 @@ bijective_higher_ranked_type! { bijective_higher_ranked_type! { pub type [][][T[][]]: TypeName[][] - for<'lt> - (DynRef<'lt, TypeName::T<'lt, T>>) - (DynRef<'lt, T>) + for<'ctx> + (DynRef<'ctx, TypeName::T<'ctx, T>>) + (DynRef<'ctx, T>) where { T: ?Sized } } bijective_higher_ranked_type! { - pub type DynMut['lt][][T['lt][]]: MaybeSized['lt][] + /// Higher ranked type for mutable borrows `&mut T`. + /// + /// The borrow gets the `'a` lifetime, not the `'ctx` lifetime. + pub type DynMut['ctx][][T['ctx][]]: WithContextLt['ctx][] for<'a> - (&'a mut MaybeSized::T<'a, 'lt, T>) + (&'a mut WithContextLt::T<'a, 'ctx, T>) (&'a mut T) where { T: ?Sized @@ -71,9 +78,9 @@ bijective_higher_ranked_type! { bijective_higher_ranked_type! { pub type [][][T[][]]: TypeName[][] - for<'lt> - (DynMut<'lt, TypeName::T<'lt, T>>) - (DynMut<'lt, T>) + for<'ctx> + (DynMut<'ctx, TypeName::T<'ctx, T>>) + (DynMut<'ctx, T>) where { T: ?Sized } @@ -81,9 +88,12 @@ bijective_higher_ranked_type! { #[cfg(feature = "alloc")] bijective_higher_ranked_type! { - pub type DynBox['lt][][T['lt][]]: MaybeSized['lt][] + /// Higher ranked type for boxes `Box<T>`. + /// + /// A [`Box`] doesn't need either lifetime. + pub type DynBox['ctx][][T['ctx][]]: WithContextLt['ctx][] for<'a> - (Box<MaybeSized::T<'a, 'lt, T>>) + (Box<WithContextLt::T<'a, 'ctx, T>>) (Box<T>) where { T: ?Sized @@ -93,134 +103,14 @@ bijective_higher_ranked_type! { #[cfg(feature = "alloc")] bijective_higher_ranked_type! { pub type [][][T[][]]: TypeName[][] - for<'lt> - (DynBox<'lt, TypeName::T<'lt, T>>) - (DynBox<'lt, T>) + for<'ctx> + (DynBox<'ctx, TypeName::T<'ctx, T>>) + (DynBox<'ctx, T>) where { T: ?Sized } } -/// [`TypeId`][core::any::TypeId] with a lifetime generic `'lt`. -/// -/// This allows comparing types that contain zero or one lifetimes. -/// When `LtTypeId::of::<A>() == LtTypeId::of::<B>()` then `A` is `B`. -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)] -pub struct LtTypeId<'lt> { - /// Invariant over `'lt` for the eq check to be correct. - /// The borrow checker is checking that the lifetimes of the type - /// IDs are the same instead of doing it at runtime, which we can't do. - _marker: PhantomData<fn(&'lt ()) -> &'lt ()>, - - /// The type ID of the name type of the type. - name_id: core::any::TypeId, - - name: &'static str, -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)] -pub struct ForAnyTypeId { - /// The type ID of the name type of the type. - name_id: core::any::TypeId, - - name: &'static str, -} - -impl<'lt> LtTypeId<'lt> { - /// Get the ID of a type. - /// - /// The type must implement [`TypeNameable`]. Note, the `'a` lifetime is **not** - /// tracked by the [`LtTypeId`], only the `'lt` lifetime is. - pub fn of<T: ?Sized + TypeName::Member<'lt>>() -> Self { - LtTypeId { - _marker: PhantomData, - name_id: core::any::TypeId::of::<TypeName::HigherRanked<'lt, T>>(), - name: core::any::type_name::<T>(), - } - } - - /// Get the type ID of the static form. - /// - /// This will be the same for all lifetimes. - pub fn as_type_id(&self) -> ForAnyTypeId { - ForAnyTypeId { - name_id: self.name_id, - name: self.name, - } - } -} - -impl<'lt> core::fmt::Display for LtTypeId<'lt> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - core::fmt::Display::fmt(self.name, f) - } -} - -/// [`Any`][core::any::Any] with a lifetime generic `'lt`. -/// -/// This trait is implemented on all types that implement -/// [`TypeNameable`]. It is not possible to implement this trait manually. -/// -/// Like [`Any`][core::any::Any] this trait can be used as a trait object with -/// downcasting. -pub trait LtAny<'lt>: sealed::Sealed<'lt> { - /// Get the [`LtTypeId`] of the type of `self`. - fn type_id(&self) -> LtTypeId<'lt>; -} - -// Prevent any impls except the following. -mod sealed { - use super::*; - - pub trait Sealed<'lt> {} - - impl<'lt, T: ?Sized + TypeName::Member<'lt>> Sealed<'lt> for T {} -} - -impl<'lt, T: ?Sized + TypeName::Member<'lt>> LtAny<'lt> for T { - fn type_id(&self) -> LtTypeId<'lt> { - LtTypeId::of::<T>() - } -} - -impl<'a, 'lt> dyn LtAny<'lt> + 'a { - /// Check if `self` is of type `T`. - pub fn is<T: ?Sized + TypeName::Member<'lt>>(&self) -> bool { - LtTypeId::of::<T>() == self.type_id() - } - - /// Downcast a `&dyn LtAny<'lt>` into a `&T`. - pub fn downcast_ref<T: TypeName::Member<'lt>>(&self) -> Option<&T> { - if self.is::<T>() { - Some(unsafe { &*(self as *const dyn LtAny<'lt> as *const T) }) - } else { - None - } - } - - /// Downcast a `&mut dyn LtAny<'lt>` into a `&mut T`. - pub fn downcast_mut<T: TypeName::Member<'lt>>(&mut self) -> Option<&mut T> { - if self.is::<T>() { - Some(unsafe { &mut *(self as *mut dyn LtAny<'lt> as *mut T) }) - } else { - None - } - } - - /// Downcast a `Box<dyn LtAny<'lt>>` into a `Box<T>`. - #[cfg(feature = "alloc")] - pub fn downcast_box<T: TypeName::Member<'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) - } - } -} - /// Dynamic trait lookup. /// /// This trait allows looking up the trait object form of `self` for a @@ -272,7 +162,7 @@ impl<'a, 'lt> dyn LtAny<'lt> + 'a { /// impl['a, 'ctx] MyNum = [dyn ToNum + 'a]; /// } /// ``` -pub trait AnyTrait<'lt> { +pub trait AnyTrait<'ctx> { /// Upcast a borrow to the given trait object. /// /// Use the `<dyn AnyTrait>::upcast()` helper method instead, if possible. @@ -280,9 +170,12 @@ pub trait AnyTrait<'lt> { /// If `self` doesn't support upcasting to the requested type /// then `None` is returned. The returned trait object is type erased so this trait /// is object safe. - fn upcast_to_id<'a>(&'a self, id: LtTypeId<'lt>) -> Option<IndirectLtAny<'a, 'lt, Ref>> + fn upcast_to_id<'a>( + &'a self, + id: TypeNameId, + ) -> Option<AnyTraitObject<'a, 'ctx, indirect::Ref>> where - 'lt: 'a; + 'ctx: 'a; /// Upcast a mutable borrow to the given trait object. /// @@ -291,52 +184,53 @@ pub trait AnyTrait<'lt> { /// If `self` doesn't support upcasting to the requested type /// then `None` is returned. The returned trait object is type erased so this trait /// is object safe. - fn upcast_to_id_mut<'a: 'b, 'b>( + fn upcast_to_id_mut<'a>( &'a mut self, - id: LtTypeId<'lt>, - ) -> Option<IndirectLtAny<'b, 'lt, Mut>> + id: TypeNameId, + ) -> Option<AnyTraitObject<'a, 'ctx, indirect::Mut>> where - 'lt: 'a; + 'ctx: 'a; } -impl<'lt> dyn AnyTrait<'lt> + Send + '_ { +impl<'b, 'ctx: 'b> dyn AnyTrait<'ctx> + Send + 'b { /// Upcast a borrow to the given trait object type. /// /// This should be used instead of [`upcast_to_id`][AnyTrait::upcast_to_id] - /// as it automatically downcasts the returned [`IndirectLtAny`]. + /// as it automatically downcasts the returned [`AnyTraitObject`]. /// - /// If the returned [`IndirectLtAny`] is the wrong type, then a panic happens. - pub fn upcast<'a, Trait: ?Sized + TypeName::Member<'lt>>( + /// If the returned [`AnyTraitObject`] is the wrong type, then a panic happens. + pub fn upcast<'a, Trait: ?Sized + TypeName::LowerType<'ctx>>( &'a self, - ) -> Option<&'a MaybeSized::T<'a, 'lt, Trait>> { - self.upcast_to_id(LtTypeId::of::<Trait>()) - .map(|object| match object.downcast::<Trait>() { + ) -> Option<&'a WithContextLt::T<'a, 'ctx, Trait>> { + self.upcast_to_id(TypeNameId::of::<Trait>()).map(|object| { + match object.downcast() { Ok(object) => object, Err(object) => panic!( "Unexpected trait object. This means a bad impl of \ `upcast_to_id`. Expected: {:?}, Got {:?}", - LtTypeId::of::<Trait>(), + TypeNameId::of::<Trait>(), object.id() ), - }) + } + }) } /// Upcast a mutable borrow to the given trait object type. /// /// This should be used instead of [`upcast_to_id_mut`][AnyTrait::upcast_to_id] - /// as it automatically downcasts the returned [`IndirectLtAny`]. + /// as it automatically downcasts the returned [`AnyTraitObject`]. /// - /// If the returned [`IndirectLtAny`] is the wrong type, then a panic happens. - pub fn upcast_mut<'a, Trait: ?Sized + TypeName::Member<'lt>>( + /// If the returned [`AnyTraitObject`] is the wrong type, then a panic happens. + pub fn upcast_mut<'a, Trait: ?Sized + TypeName::LowerType<'ctx>>( &'a mut self, - ) -> Option<&'a mut MaybeSized::T<'a, 'lt, Trait>> { - self.upcast_to_id_mut(LtTypeId::of::<Trait>()) - .map(|object| match object.downcast::<Trait>() { + ) -> Option<&'a mut WithContextLt::T<'a, 'ctx, Trait>> { + self.upcast_to_id_mut(TypeNameId::of::<Trait>()) + .map(|object| match object.downcast() { Ok(object) => object, Err(object) => panic!( "Unexpected trait object. This means a bad impl of \ `upcast_to_id_mut`. Expected: {:?}, Got {:?}", - LtTypeId::of::<Trait>(), + TypeNameId::of::<Trait>(), object.id() ), }) @@ -351,7 +245,12 @@ impl<'lt> dyn AnyTrait<'lt> + Send + '_ { #[macro_export] macro_rules! any_trait { { - impl[$lt:lifetime $($generic:tt)*] $name:ty = [$($protocol:ty),* $(,)?] $(else $fallback:path)? $(where $($bound:tt)*)? + impl[$lt:lifetime $($generic:tt)*] $name:ty = [$($protocol:ty),* $(,)?] + else { + let $id:ident; + $($fallback:tt)* + } + $(where $($bound:tt)*)? } => { impl<$lt $($generic)*> $crate::any::AnyTrait<$lt> for $name $(where $($bound)*)? @@ -359,92 +258,116 @@ macro_rules! any_trait { #[inline] fn upcast_to_id<'__>( &'__ self, - id: $crate::any::LtTypeId<$lt> - ) -> ::core::option::Option<$crate::any::IndirectLtAny<'__, $lt, $crate::any::Ref>> + id: $crate::any::TypeNameId + ) -> ::core::option::Option<$crate::any::AnyTraitObject<'__, $lt, $crate::any::indirect::Ref>> where $lt: '__ { + // This match should be optimized well by llvm. match id { - $(id if id == $crate::any::LtTypeId::of::<$protocol>() - => ::core::option::Option::Some($crate::any::IndirectLtAny::<'__, $lt, _>::new::<$protocol>(self as _)),)* - _ => { - $($fallback(id);)? - ::core::option::Option::None + $(id if id == $crate::any::TypeNameId::of::<$protocol>() + => ::core::option::Option::Some($crate::any::AnyTraitObject::<'__, $lt, _>::new::< + $crate::any::WithContextLt::T<'__, $lt, $protocol> + >(self as _)),)* + $id => { + $($fallback)* } } } #[inline] - fn upcast_to_id_mut<'__: '___, '___>( + fn upcast_to_id_mut<'__>( &'__ mut self, - id: $crate::any::LtTypeId<$lt> - ) -> ::core::option::Option<$crate::any::IndirectLtAny<'___, $lt, $crate::any::Mut>> + id: $crate::any::TypeNameId + ) -> ::core::option::Option<$crate::any::AnyTraitObject<'__, $lt, $crate::any::indirect::Mut>> where $lt: '__ { + // This match should be optimized well by llvm. match id { - $(id if id == $crate::any::LtTypeId::of::<$protocol>() - => ::core::option::Option::Some($crate::any::IndirectLtAny::<'___, $lt, _>::new::<$protocol>(self as _)),)* - _ => { - $($fallback(id);)? - ::core::option::Option::None + $(id if id == $crate::any::TypeNameId::of::<$protocol>() + => ::core::option::Option::Some($crate::any::AnyTraitObject::<'__, $lt, _>::new::< + $crate::any::WithContextLt::T<'__, $lt, $protocol> + >(self as _)),)* + $id => { + $($fallback)* } } } } }; + { + impl[$lt:lifetime $($generic:tt)*] $name:ty = [$($protocol:ty),* $(,)?] + $(where $($bound:tt)*)? + } => { + $crate::any::any_trait! { + impl[$lt $($generic)*] $name = [$($protocol),*] + else { + // Always answer no in the fallback branch if no fallback was given. + let _id; + ::core::option::Option::None + } $(where $($bound)*)? + } + } } #[doc(inline)] pub use any_trait; -/// A type erased pointer like. +use self::indirect::{sealed::RawIndirect, Indirect}; + +/// A double fat pointer. +/// +/// This struct wraps a possibly fat pointer of type described by `I`, and adds +/// an additional vtable to allow downcasting to a specific fat pointer. +/// This type is similar to if `&dyn Any` was allowed to itself store unsized types like trait +/// objects. /// -/// This acts like a `&dyn LtAny` except it is able to type erase another fat pointer. -/// This allows type erasing pointers to trait objects. A [`IndirectLtAny`] cannot -/// store an instance of itself. +/// The `'a` lifetime is the lifetime of the pointer, and the `'ctx` lifetime is a context lifetime +/// the inner type can use. This type is always invariant over both lifetimes. /// -/// The `I` generic is the flavor if pointer being used. It can be [`Ref`], [`Mut`], [`Boxed`], or -/// a custom pointer type. +/// `&'a dyn MyTrait<ctx>` becomes `AnyTraitObject<'a, 'ctx, Ref>`. +/// +/// The `I` generic is the flavor if pointer being used. It can be [`Ref`] or [`Mut`]. #[must_use] -pub struct IndirectLtAny<'a, 'lt: 'a, I: Indirect<'a>> { - info: fn() -> (LtTypeId<'lt>, unsafe fn(RawIndirect)), - indirect: RawIndirect, +pub struct AnyTraitObject<'a, 'ctx: 'a, I: Indirect<'a>> { + /// The extra vtable pointer. + /// + /// The TypeNameId gives the TypeId of the T's type name. + /// This is unique per T minus the 'a and 'ctx lifetimes. + /// which means a `dyn Trait<'ctx> + 'a` can have a TypeNameId. + /// + /// The unsafe function is the drop impl for the pointer. + /// It must only be called once per RawIndirect value, and the value must not be used after the + /// call. Only a RawIndirect of the correct I type must be passed. + info: fn() -> TypeNameId, + + /// The inner pointer value. + /// + /// This is some form of &T where T may be sized or not. + indirect: RawIndirect<'a, I>, + + /// Extra type information for the type system. _marker: PhantomData<( - I::ForT<fn(&'lt (), &'a ()) -> (&'lt (), &'a ())>, - PhantomPinned, + // Invariant over 'a and 'ctx, the TypeNameId doesn't track lifetimes so we have to do it + // here. + // https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns + fn(&'ctx ()) -> &'ctx (), + // Not Send or Sync. *const (), )>, } -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. +impl<'a, 'ctx, I: Indirect<'a>> AnyTraitObject<'a, 'ctx, I> { + /// Type erase a pointer. /// - /// The inner type `T` of the indirection is erased. - pub fn new<T: ?Sized + TypeName::Member<'lt>>( - indirect: I::ForT<MaybeSized::T<'a, 'lt, T>>, - ) -> Self { + /// `T` doesn't need to be [`Sized`]. As such, a fat pointer can be passed to this function. + pub fn new<T: ?Sized + WithContextLt::LowerType<'a, 'ctx>>(indirect: I::ForT<T>) -> Self + where + WithContextLt::HigherRanked<'a, 'ctx, T>: TypeName::LowerType<'ctx>, + { Self { - info: || { - (LtTypeId::of::<T>(), |raw| { - // SAFETY: This is only called in the drop impl. - unsafe { drop(I::from_raw::<MaybeSized::T<'a, 'lt, T>>(raw)) } - }) - }, - indirect: I::into_raw(indirect), + info: TypeNameId::of_lower::<T>, + indirect: RawIndirect::new(indirect), _marker: PhantomData, } } @@ -453,139 +376,33 @@ impl<'a, 'lt, I: Indirect<'a>> IndirectLtAny<'a, 'lt, I> { /// /// If the type of the stored value is different, then `self` is /// returned as is. - pub fn downcast<T: ?Sized + TypeName::Member<'lt>>( + pub fn downcast<T: ?Sized + WithContextLt::LowerType<'a, 'ctx>>( self, - ) -> Result<I::ForT<MaybeSized::T<'a, 'lt, T>>, Self> { - let (id, _) = (self.info)(); - - if id == LtTypeId::of::<T>() { - Ok(unsafe { I::from_raw::<MaybeSized::T<'a, 'lt, T>>(self.indirect) }) + ) -> Result<I::ForT<T>, Self> + where + WithContextLt::HigherRanked<'a, 'ctx, T>: TypeName::LowerType<'ctx>, + { + if self.id() == TypeNameId::of_lower::<T>() { + // SAFETY: We know that the type name type is unique per T because it is bijective. + // A self is only made in Self::new where the info is taken from T. + // If the check above passes then we know T must be the same minus the lifetimes. + // We know the lifetime 'ctx is correct because Self is invariant over it. + // RawIndirect makes sure that the 'a is correct by being invariant over it. + // + // See the tests at the bottom of the file for a proof that the type name is bijective + // to T. + Ok(unsafe { self.indirect.into_inner::<T>() }) } else { Err(self) } } /// Type ID of the stored value's `T`. - pub fn id(&self) -> LtTypeId<'lt> { - (self.info)().0 - } -} - -/// A pointer like type. -/// -/// For this trait the pointer like type must have the same size as a pointer. -pub unsafe trait Indirect<'a> { - /// Get the full type for a given `T`. - type ForT<T: ?Sized + 'a>: 'a; - - /// Convert the pointer into a raw indirection. - fn into_raw<T: ?Sized + 'a>(value: Self::ForT<T>) -> RawIndirect; - - /// Convert a raw indirection back into the pointer. - unsafe fn from_raw<T: ?Sized + 'a>(any: RawIndirect) -> Self::ForT<T>; -} - -/// An opaque set of bytes the size of a fat pointer. -/// -/// Repr wise this is exactly `MaybeUninit<[u8; { size of a fat pointer }]>`. -#[derive(Clone, Copy)] -#[repr(transparent)] -pub struct RawIndirect(MaybeUninit<[u8; INDIRECT_SIZE]>); - -const INDIRECT_SIZE: usize = core::mem::size_of::<usize>() * 2; - -trait Helper {} - -/// Marker type for [`IndirectLtAny`] for a borrow indirection (`&T`). -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) } - } -} - -/// Marker type for [`IndirectLtAny`] for a mutable borrow indirection (`&mut T`). -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) } + pub fn id(&self) -> TypeNameId { + (self.info)() } } -#[cfg(feature = "alloc")] -pub use boxed::*; - -#[cfg(feature = "alloc")] -mod boxed { - use super::*; - - #[cfg(not(feature = "std"))] - use alloc::boxed::Box; - - /// Marker type for [`IndirectLtAny`] for a box indirection (`Box<T>`). - 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 crate::bijective_higher_ranked_type; @@ -593,17 +410,17 @@ mod test { use super::*; #[test] - fn implementer_macro() { + fn any_trait_macro_implements_the_trait() { trait Z<'ctx> { fn get(&self) -> i32; } bijective_higher_ranked_type! { - type DynZ['ctx][]: MaybeSized['ctx][] for<'a> (dyn Z<'ctx> + 'a) + type DynZ['ctx][]: WithContextLt['ctx][] for<'a> (dyn Z<'ctx> + 'a) } bijective_higher_ranked_type! { - type [][]: TypeName[][] for<'lt> (DynZ<'lt>) + type [][]: TypeName[][] for<'ctx> (DynZ<'ctx>) } struct X<'ctx>(&'ctx i32); @@ -627,4 +444,46 @@ mod test { .unwrap(); assert_eq!(y.get(), 42); } + + // The following proves that the higher ranked types are bijective using the type system. + // + // We have the type tower: T<'a, 'ctx> <-> DynT<'ctx> <-> NameT + // We want every T, DynT, NameT set to be unique. + // + // Assume there was a U that tried to use NameT in it's type tower: + // U<'a, 'ctx> <-> DynU<'ctx> <-> NameT + // + // If we traverse the type tower in this order: T -r-> A -r-> B -l-> C -l-> D + // where -r-> is a raise and -l-> is a lower, then if D is always T we know that no sequence + // U -r-> A2 -r-> B -l-> C2 -l-> D can exist exept where T == U. This is because B cannot + // have information about where it came from and still be the same B in the type system. + // The following makes sure that a U could never become a T by the raise then lower process. + + // This proves that the bijective type names are really bijective. + fn _is_bijective_raise<'a, 'ctx: 'a, T>( + x: &WithContextLt::T< + 'a, + 'ctx, + TypeName::T<'ctx, TypeName::HigherRanked<'ctx, WithContextLt::HigherRanked<'a, 'ctx, T>>>, + >, + ) where + T: WithContextLt::LowerType<'a, 'ctx>, + WithContextLt::HigherRanked<'a, 'ctx, T>: TypeName::LowerType<'ctx>, + { + // If C -> B -> A -> B -> C (shown by this assignment), then C and A must be bijective. + let _y: &T = x; + } + + // This proves that the bijective type names are really bijective. + fn _is_bijective_lower<'a, 'ctx: 'a, U>( + x: &TypeName::HigherRanked< + 'ctx, + WithContextLt::HigherRanked<'a, 'ctx, WithContextLt::T<'a, 'ctx, TypeName::T<'ctx, U>>>, + >, + ) where + U: TypeName::MemberType, + { + // If A -> B -> C -> B -> A (shown by this assignment), then A and C must be bijective. + let _y: &U = x; + } } |