//! Interface for interfaces.
//!
//! ## Design
//! The design of protocols is based on an idea found in the
//! [`gdbstub`](https://docs.rs/gdbstub/latest/gdbstub/target/ext/index.html) crate.
//! This idea is of so called inlinable dyn extension traits.
//! However, in the form given in `gdbstub` they can't be used for arbitrary interfaces.
//! The main trait still needs to know about all the possible protocols.
//! That is where this module comes in.
//!
//! This module implements a technique we name dynamic inlinable dyn extension traits (DIDETs).
//! DIDETs adds one more layer to IDETs. Instead of a trait that knows all the possible protocols,
//! we have a single trait [`Implementer`] that allows looking up an extension trait
//! using a type ID. This may seem like it defeats the purpose of IDETs, that being to
//! make them inlinable. However, it turns out LLVM (the optimizer) is able to see
//! through this style of runtime reflection. As such, we still gain the benefits of
//! IDETs but with more flexability.
//! Protocols can now be defined in *any* crate and used between arbitrary crates.
//!
//! A protocol is a special trait that can participate as a DIDET. The only thing needed
//! for a protocol is an associated trait object. Because we need to use the
//! [`TypeId`][core::any::TypeId] of a protocol to perform reflection, we can't just use
//! the trait object itself as the protocol type. Instead an uninhabited type is used
//! as a marker for the trait.
//!
//! We then "implement" a protocol for a type by using [`Implementation`]. This provides
//! a mapping from `T` to the protocol's trait object.
//! By itself, [`Implementation`] is not enough for DIDET. A type also needs to implement
//! [`Implementer`] which allows looking up a particular [`Implementation`] trait object
//! from a [`ProtocolId`].
//!
//! The implementation of DIDETs defined by this module allows [`Implementer`] to be object safe.
//! This is done via the help of the [`AnyImpl`] type. This is not required for the core
//! idea of DIDETs.
use core::{
future::Future,
pin::{pin, Pin},
ptr,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
marker::PhantomData,
};
use crate::any::AnyTrait;
// pub mod visitor;
// pub mod walker;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
pub type Visitor<'a, 'ctx, Effect> = <<Effect as self::Effect<'ctx>>::VisitorHkt as ForLtAnyTraitSendObj<'a, 'ctx, Bound<'a, 'ctx>>>::T;
pub type Walker<'a, 'ctx, Effect> = <<Effect as self::Effect<'ctx>>::WalkerHkt as ForLtAnyTraitSendObj<'a, 'ctx, Bound<'a, 'ctx>>>::T;
pub trait AnyTraitObj<'a, 'ctx: 'a>: AnyTraitSendObj<'a, 'ctx> {
fn from_obj(value: &'a mut (dyn AnyTrait<'ctx> + 'a)) -> Self;
}
// Lifetime bound to be used in `for<'lt>` blocks.
pub type Bound<'lt, 'bound> = &'lt &'bound ();
pub trait ForLtAnyTraitSendObj<'lt, 'ctx: 'lt, B> {
type T: AnyTraitSendObj<'lt, 'ctx>;
}
pub trait HktAnyTraitSendObj<'ctx>: for<'lt> ForLtAnyTraitSendObj<'lt, 'ctx, Bound<'lt, 'ctx>> {}
impl<'ctx, T> HktAnyTraitSendObj<'ctx> for T where T: for<'lt> ForLtAnyTraitSendObj<'lt, 'ctx, Bound<'lt, 'ctx>> {}
pub trait ForLt<'lt, 'ctx: 'lt, B> {
type T;
}
pub trait Hkt<'ctx>: for<'lt> ForLt<'lt, 'ctx, Bound<'lt, 'ctx>> {}
impl<'ctx, T> Hkt<'ctx> for T where T: for<'lt> ForLt<'lt, 'ctx, Bound<'lt, 'ctx>> {}
pub trait AnyTraitSendObj<'a, 'ctx: 'a> {
fn from_obj_send(value: &'a mut (dyn AnyTrait<'ctx> + Send + 'a)) -> Self;
fn as_obj(&self) -> &dyn AnyTrait<'ctx>;
fn as_obj_mut(&mut self) -> &mut dyn AnyTrait<'ctx>;
fn into_obj(self) -> &'a mut dyn AnyTrait<'ctx>;
}
impl<'a, 'ctx: 'a> AnyTraitSendObj<'a, 'ctx> for &'a mut (dyn AnyTrait<'ctx> + 'a) {
fn from_obj_send(value: &'a mut (dyn AnyTrait<'ctx> + Send + 'a)) -> Self {
value
}
fn as_obj(&self) -> &dyn AnyTrait<'ctx> {
*self
}
fn as_obj_mut(&mut self) -> &mut dyn AnyTrait<'ctx> {
*self
}
fn into_obj(self) -> &'a mut dyn AnyTrait<'ctx> {
self
}
}
impl<'a, 'ctx: 'a> AnyTraitSendObj<'a, 'ctx> for &'a mut (dyn AnyTrait<'ctx> + Send + 'a) {
fn from_obj_send(value: &'a mut (dyn AnyTrait<'ctx> + Send + 'a)) -> Self {
value
}
fn as_obj(&self) -> &dyn AnyTrait<'ctx> {
*self
}
fn as_obj_mut(&mut self) -> &mut dyn AnyTrait<'ctx> {
*self
}
fn into_obj(self) -> &'a mut dyn AnyTrait<'ctx> {
self
}
}
impl<'a, 'ctx: 'a> AnyTraitObj<'a, 'ctx> for &'a mut (dyn AnyTrait<'ctx> + 'a) {
fn from_obj(value: &'a mut (dyn AnyTrait<'ctx> + 'a)) -> Self {
value
}
}
pub trait Effect<'ctx, C = (), B = ()>: 'static {
type VisitorHkt: HktAnyTraitSendObj<'ctx>;
type WalkerHkt: HktAnyTraitSendObj<'ctx>;
type ControlFlowHkt: Hkt<'ctx>;
}
pub type ControlFlowFor<'a, 'ctx, E = SyncEffect, C = (), B = ()> =
<<E as Effect<'ctx, C, B>>::ControlFlowHkt as ForLt<'a, 'ctx, Bound<'a, 'ctx>>>::T;
pub enum SyncEffect {}
pub struct AnyTraitSendObjHkt<'ctx>(PhantomData<fn() -> &'ctx ()>);
impl<'a, 'ctx> ForLtAnyTraitSendObj<'a, 'ctx, Bound<'a, 'ctx>> for AnyTraitSendObjHkt<'ctx> {
type T = &'a mut (dyn AnyTrait<'ctx> + Send + 'a);
}
pub struct AnyTraitObjHkt<'ctx>(PhantomData<fn() -> &'ctx ()>);
impl<'a, 'ctx> ForLtAnyTraitSendObj<'a, 'ctx, Bound<'a, 'ctx>> for AnyTraitObjHkt<'ctx> {
type T = &'a mut (dyn AnyTrait<'ctx> + 'a);
}
pub struct SyncControlFlowHkt<'ctx, C, B>(PhantomData<fn() -> (&'ctx (), C, B)>);
impl<'a, 'ctx, C, B> ForLt<'a, 'ctx, Bound<'a, 'ctx>> for SyncControlFlowHkt<'ctx, C, B> {
type T = core::ops::ControlFlow<B, C>;
}
impl<'ctx, C, B> Effect<'ctx, C, B> for SyncEffect {
type ControlFlowHkt = SyncControlFlowHkt<'ctx, C, B>;
type VisitorHkt = AnyTraitSendObjHkt<'ctx>;
type WalkerHkt = AnyTraitSendObjHkt<'ctx>;
}
macro_rules! hkt {
(for<$lt:lifetime bound by $bound:lifetime> $name:ident$([$($generic:tt)*])? = $($type:tt)*) => {
pub struct $name<$bound $(, $($generic)*)?>(core::marker::PhantomData<fn() -> (&$bound () $(, $($generic)*)?)>);
impl<$lt, $bound $(, $($generic)*)?> crate::protocol::ForLt<$lt, $bound, Bound<$lt, $bound>> for $name<$bound $(, $($generic)*)?> {
type T = $($type)*;
}
}
}
hkt!(for<'a bound by 'ctx> Demo = &'a &'ctx ());
#[cfg(feature = "alloc")]
pub struct AsyncControlFlowHkt<'ctx, C, B>(PhantomData<fn() -> (&'ctx (), C, B)>);
#[cfg(feature = "alloc")]
impl<'a, 'ctx, C, B> ForLt<'a, 'ctx, Bound<'a, 'ctx>> for AsyncControlFlowHkt<'ctx, C, B> {
type T = core::pin::Pin<
Box<dyn core::future::Future<Output = core::ops::ControlFlow<B, C>> + Send + 'a>,
>;
}
#[cfg(feature = "alloc")]
pub enum AsyncEffect {}
#[cfg(feature = "alloc")]
impl<'ctx, C, B> Effect<'ctx, C, B> for AsyncEffect {
type VisitorHkt = AnyTraitObjHkt<'ctx>;
type WalkerHkt = AnyTraitObjHkt<'ctx>;
type ControlFlowHkt = AsyncControlFlowHkt<'ctx, C, B>;
}