use core::{
fmt::Debug,
iter,
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
ptr,
};
use crate::{RecCell, RecToken, utils::Invariant};
pub struct RecBuilder<'b, 't> {
_phantom: PhantomData<(Invariant<'t>, Invariant<'b>)>,
}
impl Debug for RecBuilder<'_, '_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RecBuilder").finish_non_exhaustive()
}
}
impl<'t> RecToken<'t> {
pub fn with_builder<R, E>(
self,
scope: impl for<'b> FnOnce(RecBuilder<'b, 't>) -> Result<(R, RecBuilder<'b, 't>), E>,
) -> Result<(R, Self), E> {
scope(RecBuilder {
_phantom: PhantomData,
})
.map(|(result, _)| (result, self))
}
}
impl<'t> RecBuilder<'_, 't> {
pub fn try_add<'a, U, R, E>(
self,
uninit: U,
scope: impl for<'c> FnOnce(
RecBuilder<'c, 't>,
U::ScopeInput<'t>,
) -> Result<(R, RecBuilder<'c, 't>, U::ScopeOutput<'t>), E>,
) -> Result<(R, Self), E>
where
U: CyclicBuildable<'a>,
't: 'a,
{
let (input, output_fn) = unsafe { U::__prepare(uninit) };
let (result, _, output) = scope(
RecBuilder {
_phantom: PhantomData,
},
input,
)?;
output_fn(output);
Ok((result, self))
}
}
pub trait Sealed {}
#[allow(clippy::missing_safety_doc, reason = "sealed trait")]
pub unsafe trait CyclicBuildable<'a>: Sized + Sealed {
type ScopeInput<'t>
where
't: 'a;
type ScopeOutput<'t>
where
't: 'a;
#[doc(hidden)]
unsafe fn __prepare<'t: 'a>(
uninit: Self,
) -> (Self::ScopeInput<'t>, impl FnOnce(Self::ScopeOutput<'t>));
}
impl<T> Sealed for &'_ mut MaybeUninit<T> {}
unsafe impl<'a, T> CyclicBuildable<'a> for &'a mut MaybeUninit<T> {
type ScopeInput<'t>
= &'a RecCell<'t, T>
where
't: 'a;
type ScopeOutput<'t>
= T
where
't: 'a;
unsafe fn __prepare<'t: 'a>(
uninit: Self,
) -> (Self::ScopeInput<'t>, impl FnOnce(Self::ScopeOutput<'t>)) {
let cell = unsafe { RecCell::from_uninit(uninit) };
(cell, move |value: T| {
unsafe { cell.write_to_uninit(value) }
})
}
}
#[cfg(docsrs)]
macro_rules! maybe_tuple_doc {
($type:ident @ $item:item) => {
#[doc(fake_variadic)]
#[doc = "This trait is implemented for tuples up to twelve items long."]
$item
};
($($type:ident)* @ $item:item) => {
#[doc(hidden)]
$item
};
}
#[cfg(not(docsrs))]
macro_rules! maybe_tuple_doc {
($($type:ident)* @ $item:item) => {
$item
};
}
macro_rules! impl_tuple_cyclic_buildable {
() => {
impl Sealed for () {}
maybe_tuple_doc! {
@
unsafe impl<'a> CyclicBuildable<'a> for ()
{
type ScopeInput<'t>
= ()
where
't: 'a;
type ScopeOutput<'t>
= ()
where
't: 'a;
unsafe fn __prepare<'t: 'a>(
_: Self,
) -> (Self::ScopeInput<'t>, impl FnOnce(Self::ScopeOutput<'t>)) {
((), |()| {})
}
}
}
};
($($type:ident : $index:tt),* $(,)?) => {
impl<$($type),*> Sealed for ($($type,)*) {}
maybe_tuple_doc! {
$($type)* @
unsafe impl<'a, $($type),*> CyclicBuildable<'a> for ($($type,)*)
where
$($type: CyclicBuildable<'a>,)*
{
type ScopeInput<'t>
= ($($type::ScopeInput<'t>,)*)
where
't: 'a;
type ScopeOutput<'t>
= ($($type::ScopeOutput<'t>,)*)
where
't: 'a;
unsafe fn __prepare<'t: 'a>(
uninit: Self,
) -> (Self::ScopeInput<'t>, impl FnOnce(Self::ScopeOutput<'t>)) {
let prepared = unsafe {
($(CyclicBuildable::__prepare(uninit.$index),)*)
};
(($ (prepared.$index.0,)*), move |output| {
$(prepared.$index.1(output.$index);)*
})
}
}
}
};
}
macro_rules! impl_tuple_cyclic_buildables {
($($type:ident : $index:tt),* $(,)?) => {
impl_tuple_cyclic_buildable!();
impl_tuple_cyclic_buildables!(@impl (); $(($type : $index))*);
};
(@impl ($($done:tt)*);) => {};
(@impl ($($done:tt)*); ($type:ident : $index:tt) $($rest:tt)*) => {
impl_tuple_cyclic_buildable!($($done)* $type : $index);
impl_tuple_cyclic_buildables!(
@impl ($($done)* $type : $index,); $($rest)*
);
};
}
impl_tuple_cyclic_buildables!(
T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10,
T11: 11
);
impl<T, const N: usize> Sealed for [T; N] {}
unsafe impl<'a, T, const N: usize> CyclicBuildable<'a> for [T; N]
where
T: CyclicBuildable<'a>,
{
type ScopeInput<'t>
= [T::ScopeInput<'t>; N]
where
't: 'a;
type ScopeOutput<'t>
= [T::ScopeOutput<'t>; N]
where
't: 'a;
unsafe fn __prepare<'t: 'a>(
uninit: Self,
) -> (Self::ScopeInput<'t>, impl FnOnce(Self::ScopeOutput<'t>)) {
let uninit = ManuallyDrop::new(uninit);
let mut inputs = [const { MaybeUninit::uninit() }; N];
let mut finishes = [const { MaybeUninit::uninit() }; N];
for i in 0..N {
let uninit = unsafe { ptr::read(&raw const uninit[i]) };
let (input, finish) = unsafe { T::__prepare(uninit) };
inputs[i].write(input);
finishes[i].write(finish);
}
let inputs = unsafe { MaybeUninit::from(inputs).assume_init() };
let finishes = unsafe { MaybeUninit::from(finishes).assume_init() };
(inputs, move |outputs| {
for (finish, output) in iter::zip(finishes, outputs) {
finish(output);
}
})
}
}