supply 0.1.0

Provider API for arbitrary number of lifetimes.
Documentation
use core::marker::PhantomData;

use erased::Erased;

use crate::lt_list::Subset;
use crate::tag::{TagTypeId, WithLt};
use crate::LtList;

mod erased;

pub struct MutErasedWide<'u, L: LtList> {
    _lt: PhantomData<&'u mut dyn core::any::Any>,
    borrow: Erased<{ core::mem::size_of::<&mut dyn core::any::Any>() }>,
    tag_type_id: TagTypeId<L>,
}

impl<'u, L: LtList> MutErasedWide<'u, L> {
    pub fn new<T: ?Sized + WithLt<TL> + 'static, TL: LtList + Subset<L>>(
        borrow: &'u mut T::Reified,
    ) -> Self {
        Self {
            _lt: PhantomData,
            borrow: Erased::new::<&'u mut T::Reified>(borrow),
            tag_type_id: TagTypeId::<L>::of::<T, TL>(),
        }
    }

    pub fn downcast<T: ?Sized + WithLt<TL> + 'static, TL: LtList + Subset<L>>(
        self,
    ) -> Result<&'u mut T::Reified, Self> {
        if self.tag_type_id.is_same(&TagTypeId::<L>::of::<T, TL>()) {
            // SAFETY:
            // For this to be sound we need the value in `self.borrow` to be *exactly*
            // `&'u mut T::Reified`. This consists of 2 parts.
            //   1. The type `T` must be the one used in `new`.
            //   2. The type `TL` (including the contained lifetimes) must be the one used in `new`.
            //
            // We check the first by having `tag_type_id` store a `TypeId` for `T` and check it for
            // equality (see `TagTypeId::is_same`).
            //
            // We check the second in two parts. First we have `TagTypeId` store a `'static` tag
            // type's `TypeId` to check in `is_same`. This checks that `TL` is the same "structure"
            // as the one used in `new` (see the `TaggedLifetime` trait for this tagging).
            // This leaves the actual lifetimes in `TL`. These are checked by connecting them to
            // `L` via the `SuperSet` trait (must be correctly implemented) and then having the
            // borrow checker verify that they are the same. This is possible because all `LtList`
            // implementers are invariant over their lifetime, and `is_same` requires the `L`
            // generic be the same between the right and left sides.
            Ok(unsafe { self.borrow.into_inner::<&'u mut T::Reified>() })
        } else {
            Err(self)
        }
    }
}

// #[test]
// fn demo() {
//     let x = {
//         let x = String::from("hello");
//         let y = x.as_str();
//
//         {
//             let mut opt = TaggedOption::<Lt1, tag_for!(<'a> &'a str)>::new(None);
//
//             // Erase type (keep lifetimes?)
//             let a: MutErasedWide<Lt1> = MutErasedWide::<Lt1>::new::<
//                 tag_for!(<'b> TaggedOption<Lt1<'b>, tag_for!(<'a> &'a str)>),
//                 Lt1
//             >(&mut opt);
//
//             // Get back type
//             let Ok(z) = a.downcast::<tag_for!(<'b> TaggedOption<Lt1<'b>, tag_for!(<'a> &'a str)>), Lt1>() else {
//                 panic!()
//             };
//
//             // Insert value
//             z.fulfill(y);
//
//             opt.into_inner().unwrap()
//         }
//     };
//     dbg!(x);
//     todo!();
// }