use core::{
cell::UnsafeCell,
fmt::Debug,
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
};
#[cfg(feature = "cyclic_with_mut")]
use replace_with::replace_with_or_abort_and_return;
use crate::{
CyclicBuildable,
utils::{Invariant, into_ok},
};
macro_rules! cast_ref {
($r:expr) => {{
match $r {
r => {
if false {
cast_ref_lifetime_check(r)
} else {
#[allow(clippy::ptr_as_ptr, clippy::ref_as_ptr)]
&*(r as *const _ as *const _)
}
}
}
}};
(mut $r:expr) => {{
match $r {
r => {
if false {
cast_mut_lifetime_check(r)
} else {
#[allow(clippy::ptr_as_ptr, clippy::ref_as_ptr)]
&mut *(r as *mut _ as *mut _)
}
}
}
}};
}
const fn cast_ref_lifetime_check<T: ?Sized, U: ?Sized>(_: &T) -> &U {
unimplemented!()
}
const fn cast_mut_lifetime_check<T: ?Sized, U: ?Sized>(_: &mut T) -> &mut U {
unimplemented!()
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct RecToken<'t> {
_phantom: Invariant<'t>,
}
impl<'t> RecToken<'t> {
#[allow(clippy::new_ret_no_self, reason = "same conventions as GhostToken")]
pub fn new<R>(scope: impl FnOnce(RecToken<'_>) -> R) -> R {
scope(RecToken {
_phantom: PhantomData,
})
}
pub fn make_cyclic<'a, U, R>(
self,
uninit: U,
scope: impl FnOnce(U::ScopeInput<'t>) -> (R, U::ScopeOutput<'t>),
) -> (R, Self)
where
U: CyclicBuildable<'a>,
't: 'a,
{
into_ok(self.try_make_cyclic(uninit, move |input| Ok(scope(input))))
}
#[cfg(feature = "cyclic_with_mut")]
pub fn make_cyclic_with_mut<'a, U, R>(
&mut self,
uninit: U,
scope: impl FnOnce(U::ScopeInput<'t>) -> (R, U::ScopeOutput<'t>),
) -> R
where
U: CyclicBuildable<'a>,
't: 'a,
{
replace_with_or_abort_and_return(self, move |token| token.make_cyclic(uninit, scope))
}
pub fn try_make_cyclic<'a, U, R, E>(
self,
uninit: U,
scope: impl FnOnce(U::ScopeInput<'t>) -> Result<(R, U::ScopeOutput<'t>), E>,
) -> Result<(R, Self), E>
where
U: CyclicBuildable<'a>,
't: 'a,
{
self.with_builder(move |builder| {
builder.try_add(uninit, move |builder, input| {
let (result, output) = scope(input)?;
Ok((result, builder, output))
})
})
}
}
impl Debug for RecToken<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RecToken").finish_non_exhaustive()
}
}
#[repr(transparent)]
pub struct RecCell<'t, T> {
_phantom: Invariant<'t>,
data: UnsafeCell<MaybeUninit<T>>,
}
impl<'t, T> RecCell<'t, T> {
pub const fn new(value: T) -> Self {
Self {
_phantom: PhantomData,
data: UnsafeCell::new(MaybeUninit::new(value)),
}
}
pub const fn into_inner(self) -> T {
let this = ManuallyDrop::new(self);
unsafe { (&raw const this).cast::<T>().read() }
}
#[cfg_attr(
feature = "cyclic_with_mut",
doc = "If you want to use `&mut RecToken`, consider [`RecCell::new_cyclic_with_mut`]."
)]
pub fn new_cyclic<'a, R>(
uninit: &'a mut MaybeUninit<T>,
token: RecToken<'t>,
scope: impl FnOnce(&'a Self) -> (R, T),
) -> (R, RecToken<'t>)
where
't: 'a,
{
token.make_cyclic(uninit, scope)
}
#[cfg(feature = "cyclic_with_mut")]
pub fn new_cyclic_with_mut<'a, R>(
uninit: &'a mut MaybeUninit<T>,
token: &mut RecToken<'t>,
scope: impl FnOnce(&'a Self) -> (R, T),
) -> R
where
't: 'a,
{
replace_with_or_abort_and_return(token, move |token| Self::new_cyclic(uninit, token, scope))
}
pub fn try_new_cyclic<'a, R, E>(
uninit: &'a mut MaybeUninit<T>,
token: RecToken<'t>,
scope: impl FnOnce(&'a Self) -> Result<(R, T), E>,
) -> Result<(R, RecToken<'t>), E>
where
't: 'a,
{
token.try_make_cyclic(uninit, scope)
}
pub const fn from_mut(value: &mut T) -> &mut Self {
unsafe { cast_ref!(mut value) }
}
pub const fn from_slice_mut(value: &mut [T]) -> &mut [Self] {
unsafe { cast_ref!(mut value) }
}
pub const fn get_mut(&mut self) -> &mut T {
unsafe { cast_ref!(mut self) }
}
pub const fn get_slice_mut(this: &mut [Self]) -> &mut [T] {
unsafe { cast_ref!(mut this) }
}
pub const fn as_ptr(&self) -> *mut T {
self.data.get().cast()
}
pub const fn as_slice_ptr(this: &[Self]) -> *mut [T] {
let cells = core::ptr::from_ref(this) as *const UnsafeCell<[MaybeUninit<T>]>;
UnsafeCell::raw_get(cells) as *mut [T]
}
pub const fn borrow<'a>(&'a self, _: &'a RecToken<'t>) -> &'a T {
unsafe { &*self.as_ptr() }
}
pub const fn borrow_slice<'a>(this: &'a [Self], _: &'a RecToken<'t>) -> &'a [T] {
unsafe { &*Self::as_slice_ptr(this) }
}
#[allow(clippy::mut_from_ref, reason = "the whole point of this crate")]
pub const fn borrow_mut<'a>(&'a self, _: &'a mut RecToken<'t>) -> &'a mut T {
unsafe { &mut *self.as_ptr() }
}
pub const fn borrow_slice_mut<'a>(this: &'a [Self], _: &'a mut RecToken<'t>) -> &'a mut [T] {
unsafe { &mut *Self::as_slice_ptr(this) }
}
pub const unsafe fn from_uninit(uninit: &mut MaybeUninit<T>) -> &Self {
unsafe { cast_ref!(mut uninit) }
}
pub const unsafe fn write_to_uninit(&self, value: T) {
unsafe { self.as_ptr().write(value) }
}
}
impl<'t, T, const N: usize> RecCell<'t, [T; N]> {
pub const fn as_array_of_cells(&self) -> &[RecCell<'t, T>; N] {
unsafe { cast_ref!(self) }
}
pub const fn as_array_of_cells_mut(&mut self) -> &mut [RecCell<'t, T>; N] {
unsafe { cast_ref!(mut self) }
}
pub const fn from_array_of_cells<'a>(cells: &'a [RecCell<'t, T>; N]) -> &'a Self {
unsafe { cast_ref!(cells) }
}
pub const fn from_array_of_cells_mut<'a>(cells: &'a mut [RecCell<'t, T>; N]) -> &'a mut Self {
unsafe { cast_ref!(mut cells) }
}
}
unsafe impl<T: Send> Send for RecCell<'_, T> {}
unsafe impl<T: Send + Sync> Sync for RecCell<'_, T> {}
impl<T> Drop for RecCell<'_, T> {
fn drop(&mut self) {
unsafe { self.as_ptr().drop_in_place() }
}
}
impl<T: AsMut<U>, U: ?Sized> AsMut<U> for RecCell<'_, T> {
fn as_mut(&mut self) -> &mut U {
self.get_mut().as_mut()
}
}
impl<T> From<T> for RecCell<'_, T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: Default> Default for RecCell<'_, T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> Debug for RecCell<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RecCell").finish_non_exhaustive()
}
}