Diffstat (limited to 'src/any.rs')
-rw-r--r--src/any.rs585
1 files changed, 222 insertions, 363 deletions
diff --git a/src/any.rs b/src/any.rs
index 72a0c5f..da0f369 100644
--- a/src/any.rs
+++ b/src/any.rs
@@ -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;
+ }
}