Diffstat (limited to 'src/any.rs')
| -rw-r--r-- | src/any.rs | 595 |
1 files changed, 103 insertions, 492 deletions
@@ -2,535 +2,146 @@ //! //! The `AnyTrait` trait provides dynamic upcasting to trait objects. -pub mod indirect; +mod erased; +mod mut_any_unsized; +mod ref_any_unsized; mod static_wrapper; +pub mod type_name; mod type_name_id; -use crate::hkt::{Invariant, Marker}; -use core::marker::PhantomData; +use crate::hkt::Marker; -use effectful::{ - bound::IsSync, - environment::{DynBind, EnvConfig, Environment, InEnvironment}, - SendSync, -}; +pub use mut_any_unsized::*; +pub use ref_any_unsized::*; pub use static_wrapper::*; pub use type_name_id::*; -#[cfg(all(feature = "alloc", not(feature = "std")))] -use alloc::boxed::Box; - -pub enum StaticType {} -pub enum NamedType {} -pub enum LifetimeType {} - -#[allow(non_snake_case)] -pub mod TypeName { - use effectful::environment::{DynBind, EnvConfig}; - - pub trait MemberTypeForLt<'a, 'ctx: 'a, E: EnvConfig, B> { - type T: ?Sized + LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx (), Higher = Self>; - } - - pub trait MemberType<E: EnvConfig>: - 'static + for<'a, 'ctx> MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()> +pub trait AnyTrait<'ctx> { + fn upcast_by_id<'a, 'lt: 'a>( + &'a self, + id: WithLtTypeId<'lt, 'ctx>, + ) -> Option<RefAnyUnsized<'a, 'lt, 'ctx>> + where + 'ctx: 'lt, { + let _id = id; + None } - impl<T: ?Sized, E: EnvConfig> MemberType<E> for T where - T: 'static + for<'a, 'ctx> MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()> + fn upcast_by_id_mut<'a, 'lt: 'a>( + &'a mut self, + id: WithLtTypeId<'lt, 'ctx>, + ) -> Option<MutAnyUnsized<'a, 'lt, 'ctx>> + where + 'ctx: 'lt, { + let _id = id; + None } +} - pub trait LowerTypeWithBound<'a, 'ctx: 'a, E: EnvConfig, B>: 'a { - type Higher: ?Sized + MemberTypeForLt<'a, 'ctx, E, &'a &'ctx (), T = Self> + 'static; - } - - pub trait LowerType<'a, 'ctx: 'a, E: EnvConfig>: - LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx ()> - { +impl<'lt, 'ctx: 'lt> dyn AnyTrait<'ctx> + 'lt { + #[track_caller] + pub fn upcast<'a, T: ?Sized + type_name::WithLt<'lt, 'ctx>>(&'a self) -> Option<&'a T> { + self.upcast_by_id(WithLtTypeId::of::<T>()) + .map(|value| match value.downcast::<T>() { + Ok(value) => value, + Err(_) => panic!("wrong type returned by upcast_by_id"), + }) } - impl<'a, 'ctx: 'a, T: ?Sized, E: EnvConfig> LowerType<'a, 'ctx, E> for T where - T: LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx ()> - { + #[track_caller] + pub fn upcast_mut<'a, T: ?Sized + type_name::WithLt<'lt, 'ctx>>( + &'a mut self, + ) -> Option<&'a mut T> { + self.upcast_by_id_mut(WithLtTypeId::of::<T>()) + .map(|value| match value.downcast::<T>() { + Ok(value) => value, + Err(_) => panic!("wrong type returned by upcast_by_id_mut"), + }) } - - pub type T<'a, 'ctx, __, E> = <__ as MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()>>::T; - pub type HigherRanked<'a, 'ctx, __, E> = - <__ as LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx ()>>::Higher; } -#[derive(SendSync)] -pub struct RefHrt<T: ?Sized>(Marker<T>); - -impl<'a, 'lt, T: ?Sized, E: EnvConfig, B> TypeName::MemberTypeForLt<'a, 'lt, E, &'a &'lt B> - for RefHrt<T> -where - T: TypeName::MemberTypeForLt<'a, 'lt, E, &'a &'lt B>, - <T as TypeName::MemberTypeForLt<'a, 'lt, E, &'a &'lt B>>::T: IsSync<E::NeedSend>, -{ - type T = &'a <T as TypeName::MemberTypeForLt<'a, 'lt, E, &'a &'lt B>>::T; -} - -impl<'a, 'lt, T: ?Sized, E: EnvConfig, B> TypeName::LowerTypeWithBound<'a, 'lt, E, &'a &'lt B> - for &'a T -where - T: TypeName::LowerTypeWithBound<'a, 'lt, E, &'a &'lt B>, - T: IsSync<E::NeedSend>, -{ - type Higher = RefHrt<<T as TypeName::LowerTypeWithBound<'a, 'lt, E, &'a &'lt B>>::Higher>; -} +const _: () = { + pub struct Ref<T: ?Sized>(Marker<T>); -#[derive(SendSync)] -pub struct MutHrt<T: ?Sized>(Marker<T>); - -impl<'a, 'lt, T: ?Sized, E: EnvConfig, B> TypeName::MemberTypeForLt<'a, 'lt, E, &'a &'lt B> - for MutHrt<T> -where - T: TypeName::MemberTypeForLt<'a, 'lt, E, &'a &'lt B>, -{ - type T = &'a mut <T as TypeName::MemberTypeForLt<'a, 'lt, E, &'a &'lt B>>::T; -} - -impl<'a, 'lt, T: ?Sized, E: EnvConfig, B> TypeName::LowerTypeWithBound<'a, 'lt, E, &'a &'lt B> - for &'a mut T -where - T: TypeName::LowerTypeWithBound<'a, 'lt, E, &'a &'lt B>, -{ - type Higher = MutHrt<<T as TypeName::LowerTypeWithBound<'a, 'lt, E, &'a &'lt B>>::Higher>; -} - -#[cfg(feature = "alloc")] -impl<'a, 'ctx, E: EnvConfig, T: ?Sized> TypeName::MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()> - for Box<T> -where - T: TypeName::MemberTypeForLt<'a, 'ctx, E, &'a &'ctx ()>, -{ - type T = Box<TypeName::T<'a, 'ctx, T, E>>; -} - -#[cfg(feature = "alloc")] -impl<'a, 'ctx, E: EnvConfig, T: ?Sized> TypeName::LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx ()> - for Box<T> -where - T: TypeName::LowerTypeWithBound<'a, 'ctx, E, &'a &'ctx ()>, -{ - type Higher = Box<TypeName::HigherRanked<'a, 'ctx, T, E>>; -} - -/// Dynamic trait lookup. -/// -/// This trait allows looking up the trait object form of `self` for a -/// given trait object `id`. This is similar to upcasting to the trait given -/// by `id` if [`AnyTrait`] had every trait as a super bound. -/// -// ``` -// use treaty::any::{AnyTrait, any_trait, TypeName}; -// use treaty::hkt::higher_ranked_type; -// -// // Create a test value. -// let my_num = MyNum(42); -// -// // Cast to be a AnyTrait trait object. -// // Now we don't know the type. -// let anything: &(dyn AnyTrait<'_> + Send + Sync) = &my_num; -// -// // We can still upcast to an impl of ToNum. -// let to_num_object: &dyn ToNum = anything.upcast::<DynToNum>().unwrap(); -// -// assert_eq!(to_num_object.num(), 42); -// -// // === Type Setup === -// -// // An example trait. -// trait ToNum { -// fn num(&self) -> i32; -// } -// -// enum DynToNum {} -// -// higher_ranked_type! { -// impl TypeName { -// impl['a, 'ctx] type T['a, 'ctx] for DynToNum = -// dyn ToNum + Send + Sync + 'a; -// -// impl['a, 'ctx] type HigherRanked['a, 'ctx] for dyn ToNum + Send + Sync + 'a = -// DynToNum; -// } -// } -// -// // An example struct. -// struct MyNum(i32); -// -// // The example struct impls the example trait. -// impl ToNum for MyNum { -// fn num(&self) -> i32 { -// self.0 -// } -// } -// -// // Allow the example struct's trait impls to be looked up at runtime. -// // Here the only trait that can be looked up is ToNum as its the only -// // one in the list. -// any_trait! { -// impl['ctx] MyNum = [DynToNum] -// } -// ``` -pub trait AnyTrait<'ctx, Env: EnvConfig>: DynBind<Env> { - /// The trait objects this type can be upcasted to. - type Available + impl<'lt, 'ctx, T: ?Sized> type_name::Lower<'lt, 'ctx, &'lt &'ctx ()> for Ref<T> where - Self: Sized; + T: type_name::Lower<'lt, 'ctx, &'lt &'ctx ()>, + { + type Lowered = &'lt type_name::Lowered<'lt, 'ctx, T>; + } - /// Upcast a borrow to the given trait object. - /// - /// Use the `<dyn AnyTrait>::upcast()` helper method instead, if possible. - /// - /// 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: TypeNameId, - ) -> Option<AnyTraitObject<'a, 'ctx, indirect::Ref, Env>> + impl<'lt, 'ctx, T: ?Sized> type_name::Raise<'lt, 'ctx, &'lt &'ctx ()> for &'lt T where - 'ctx: 'a; + T: type_name::Raise<'lt, 'ctx, &'lt &'ctx ()>, + { + type Raised = Ref<type_name::Raised<'lt, 'ctx, T>>; + } +}; - /// Upcast a mutable borrow to the given trait object. - /// - /// Use the `<dyn AnyTrait>::upcast_mut()` helper method instead, if possible. - /// - /// 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>( - &'a mut self, - id: TypeNameId, - ) -> Option<AnyTraitObject<'a, 'ctx, indirect::Mut, Env>> - where - 'ctx: 'a; -} +const _: () = { + pub struct Mut<T: ?Sized>(Marker<T>); -impl<'b, 'ctx: 'b, Env: Environment> dyn AnyTrait<'ctx, Env> + '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 [`AnyTraitObject`]. - /// - /// If the returned [`AnyTraitObject`] is the wrong type, then a panic happens. - #[inline(always)] - pub fn upcast<'a, Trait: ?Sized + TypeName::MemberType<Env>>( - &'a self, - ) -> Option<&'a TypeName::T<'a, 'ctx, Trait, Env>> { - self.upcast_to_id(TypeNameId::of::<Trait, Env>()) - .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 {:?}", - TypeNameId::of::<Trait, Env>(), - object.id() - ), - }) + impl<'lt, 'ctx, T: ?Sized> type_name::Lower<'lt, 'ctx, &'lt &'ctx ()> for Mut<T> + where + T: type_name::Lower<'lt, 'ctx, &'lt &'ctx ()>, + { + type Lowered = &'lt mut type_name::Lowered<'lt, 'ctx, T>; } - /// 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 [`AnyTraitObject`]. - /// - /// If the returned [`AnyTraitObject`] is the wrong type, then a panic happens. - #[inline(always)] - pub fn upcast_mut<'a, Trait: ?Sized + TypeName::MemberType<Env>>( - &'a mut self, - ) -> Option<&'a mut TypeName::T<'a, 'ctx, Trait, Env>> { - self.upcast_to_id_mut(TypeNameId::of::<Trait, Env>()) - .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 {:?}", - TypeNameId::of::<Trait, Env>(), - object.id() - ), - }) + impl<'lt, 'ctx, T: ?Sized> type_name::Raise<'lt, 'ctx, &'lt &'ctx ()> for &'lt mut T + where + T: type_name::Raise<'lt, 'ctx, &'lt &'ctx ()>, + { + type Raised = Mut<type_name::Raised<'lt, 'ctx, T>>; } -} +}; -/// Implement [`AnyTrait`] for a type. -/// -/// This allows looking up trait objects from the provided list. -/// See [`AnyTrait`] for an example. #[doc(hidden)] #[macro_export] -macro_rules! any_trait { +macro_rules! trait_by_id { { - impl[$lt:lifetime $($generic:tt)*][$env:ident] $name:ty = [$($protocol:ty),* $(,)?] - ref { - let ($if_this:ident, $if_id:ident); - $($if_fallback:tt)* - } - else ref { - $($fallback:tt)* - } - mut { - let ($if_mut_this:ident, $if_mut_id:ident); - $($if_mut_fallback:tt)* - } - else mut { - $($mut_fallback:tt)* - } - $(where $($bound:tt)*)? - } => { - impl<$lt $($generic)*, $env> $crate::any::AnyTrait<$lt, $env> for $name - $(where $($bound)*)? + & + $this:ident, + $id:ident, { - type Available = ( - $($protocol,)* - ); - - #[inline(always)] - fn upcast_to_id<'__>( - &'__ self, - id: $crate::any::TypeNameId - ) -> ::core::option::Option<$crate::any::AnyTraitObject<'__, $lt, $crate::any::indirect::Ref, $env>> - where - $lt: '__ - { - let ($if_this, $if_id) = (self, id); - - { - $($if_fallback)* - } + type Impls<$lt:lifetime, $ctx:lifetime> = ($($trait:ty),* $(,)?); - // This match should be optimized well by llvm. - match $if_id { - $(id if id == $crate::any::TypeNameId::of::<$protocol, $env>() - => ::core::option::Option::Some($crate::any::AnyTraitObject::<'__, $lt, _, $env>::new::< - $crate::any::TypeName::T<'__, $lt, $protocol, $env> - >($if_this as _)),)* - _ => { - $($fallback)* - } - } - } - - #[inline(always)] - fn upcast_to_id_mut<'__>( - &'__ mut self, - id: $crate::any::TypeNameId - ) -> ::core::option::Option<$crate::any::AnyTraitObject<'__, $lt, $crate::any::indirect::Mut, $env>> - where - $lt: '__ - { - let ($if_mut_this, $if_mut_id) = (self, id); - - { - $($if_mut_fallback)* - } - - // This match should be optimized well by llvm. - match $if_mut_id { - $(id if id == $crate::any::TypeNameId::of::<$protocol, $env>() - => ::core::option::Option::Some($crate::any::AnyTraitObject::<'__, $lt, _, $env>::new::< - $crate::any::TypeName::T<'__, $lt, $protocol, $env> - >($if_mut_this as _)),)* - _ => { - $($mut_fallback)* - } - } + $($body:tt)* + } + } => {{ + match $id { + $( + $id if $id == $crate::any::WithLtTypeId::of::<$trait>() => { + Some($crate::any::RefAnyUnsized::new($this as &$trait)) } + )* + _ => { $($body)* } } - }; + }}; { - impl[$lt:lifetime $($generic:tt)*][$env:ident] $name:ty = [$($protocol:ty),* $(,)?] - $(where $($bound:tt)*)? - } => { - $crate::any::any_trait! { - impl[$lt $($generic)*][$env] $name = [$($protocol),*] - ref { - let (_this, _id); - } else ref { - // Always answer no in the fallback branch if no fallback was given. - ::core::option::Option::None - } mut { - let (_this, _id); - } else mut { - // Always answer no in the fallback branch if no fallback was given. - ::core::option::Option::None - } $(where $($bound)*)? - } - } -} -#[doc(inline)] -pub use any_trait; - -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. -/// -/// 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. -/// -/// `&'a dyn MyTrait<ctx>` becomes `AnyTraitObject<'a, 'ctx, Ref>`. -/// -/// The `I` generic is the flavor if pointer being used. It can be [`Ref`][indirect::Ref] or [`Mut`][indirect::Mut]. -#[must_use] -pub struct AnyTraitObject<'a, 'ctx: 'a, I: Indirect<'a>, E> { - /// 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>, - - _lifetime: Invariant<'ctx>, - _not_send_sync: PhantomData<*const ()>, - _marker: Marker<E>, -} + &mut + $this:ident, + $id:ident, + { + type Impls<$lt:lifetime, $ctx:lifetime> = ($($trait:ty),* $(,)?); -impl<'a, 'ctx, I: Indirect<'a>, E: EnvConfig> AnyTraitObject<'a, 'ctx, I, E> { - /// Type erase a pointer. - /// - /// `T` doesn't need to be [`Sized`]. As such, a fat pointer can be passed to this function. - pub fn new<T: ?Sized + TypeName::LowerType<'a, 'ctx, E>>(indirect: I::ForT<T>) -> Self { - Self { - info: TypeNameId::of_lower::<T, E>, - indirect: RawIndirect::new(indirect), - _lifetime: Default::default(), - _not_send_sync: PhantomData, - _marker: Default::default(), + $($body:tt)* } - } - - /// 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 + TypeName::LowerType<'a, 'ctx, E>>( - self, - ) -> Result<I::ForT<T>, Self> { - if self.id() == TypeNameId::of_lower::<T, E>() { - // 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. - #[allow(unsafe_code)] - Ok(unsafe { self.indirect.into_inner::<T>() }) - } else { - Err(self) + } => {{ + match $id { + $( + $id if { + eprintln!("a: {:?}\nb: {:?}", &$id, $crate::any::WithLtTypeId::of::<$trait>()); + $id == $crate::any::WithLtTypeId::of::<$trait>() + }=> { + Some($crate::any::MutAnyUnsized::new($this as &mut $trait)) + } + )* + _ => { $($body)* } } - } - - /// Type ID of the stored value's `T`. - pub fn id(&self) -> TypeNameId { - (self.info)() - } -} - -#[cfg(test)] -mod test { - use super::*; - - // #[test] - // fn any_trait_macro_implements_the_trait() { - // trait Z<'ctx> { - // fn get(&self) -> i32; - // } - // - // struct DynZ; - // - // higher_ranked_type! { - // impl TypeName { - // impl['a, 'ctx] type T['a, 'ctx] for DynZ = - // dyn Z<'ctx> + Send + Sync + 'a; - // - // impl['a, 'ctx] type HigherRanked['a, 'ctx] for dyn Z<'ctx> + Send + Sync + 'a = - // DynZ; - // } - // } - // - // struct X<'ctx>(&'ctx i32); - // - // impl<'ctx> Z<'ctx> for X<'ctx> { - // fn get(&self) -> i32 { - // *self.0 - // } - // } - // - // any_trait! { - // impl['ctx] X<'ctx> = [ - // DynZ - // ] - // } - // - // let z = 42; - // let x = X(&z); - // let y = (&x as &(dyn AnyTrait<'_> + Send + Sync)) - // .upcast::<DynZ>() - // .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, E: Environment>( - x: &TypeName::T<'a, 'ctx, TypeName::HigherRanked<'a, 'ctx, T, E>, E>, - ) where - T: TypeName::LowerType<'a, 'ctx, E>, - { - // 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, E: Environment>( - x: &TypeName::HigherRanked<'a, 'ctx, TypeName::T<'a, 'ctx, U, E>, E>, - ) where - U: TypeName::MemberType<E>, - { - // If A -> B -> C -> B -> A (shown by this assignment), then A and C must be bijective. - let _y: &U = x; - } - - // fn _is_bijective_raise2<'a, 'ctx: 'a, 'b, 'c: 'b, T>( - // x: &TypeName::T<'b, 'c, TypeName::HigherRanked<'a, 'ctx, T>>, - // ) where - // T: TypeName::LowerType<'a, 'ctx>, - // { - // // If C -> B -> A -> B -> C (shown by this assignment), then C and A must be bijective. - // let _y: &T = x; - // } + }}; } +#[doc(inline)] +pub use trait_by_id; |