use core::{cell::UnsafeCell, marker::PhantomData, mem};
pub struct GhostToken<'brand> {
_marker: InvariantLifetime<'brand>,
}
impl<'brand> GhostToken<'brand> {
#[allow(clippy::new_ret_no_self)]
pub fn new<R, F>(fun: F) -> R
where
for<'new_brand> F: FnOnce(GhostToken<'new_brand>) -> R,
{
let token = Self {
_marker: InvariantLifetime::default(),
};
fun(token)
}
}
unsafe impl<'brand> Send for GhostToken<'brand> {}
unsafe impl<'brand> Sync for GhostToken<'brand> {}
#[repr(transparent)]
pub struct GhostCell<'brand, T: ?Sized> {
_marker: InvariantLifetime<'brand>,
value: UnsafeCell<T>,
}
impl<'brand, T> GhostCell<'brand, T> {
pub const fn new(value: T) -> Self {
let _marker = PhantomData;
let value = UnsafeCell::new(value);
Self { _marker, value }
}
pub fn into_inner(self) -> T {
self.value.into_inner()
}
}
impl<'brand, T: ?Sized> GhostCell<'brand, T> {
pub fn borrow<'a>(&'a self, _: &'a GhostToken<'brand>) -> &'a T {
unsafe { &*self.value.get() }
}
pub fn borrow_mut<'a>(&'a self, _: &'a mut GhostToken<'brand>) -> &'a mut T {
unsafe { &mut *self.value.get() }
}
pub const fn as_ptr(&self) -> *mut T {
self.value.get()
}
pub fn get_mut(&mut self) -> &mut T {
unsafe { mem::transmute(self) }
}
pub fn from_mut(t: &mut T) -> &mut Self {
unsafe { mem::transmute(t) }
}
}
#[forbid(unsafe_code)]
impl<'brand, T> GhostCell<'brand, T> {
pub fn replace(&self, value: T, token: &mut GhostToken<'brand>) -> T {
mem::replace(self.borrow_mut(token), value)
}
pub fn take(&self, token: &mut GhostToken<'brand>) -> T
where
T: Default,
{
self.replace(T::default(), token)
}
#[cfg(feature = "experimental-multiple-mutable-borrows")]
pub fn swap(
&self,
other: &Self,
token: &mut GhostToken<'brand>,
) -> Result<(), crate::ghost_borrow_mut::GhostAliasingError> {
if core::ptr::eq(self, other) {
return Ok(());
}
crate::ghost_borrow_mut::GhostBorrowMut::borrow_mut((self, other), token).map(|(a, b)| mem::swap(a, b))
}
}
impl<'brand, T: Default> Default for GhostCell<'brand, T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<'brand, T> GhostCell<'brand, [T]> {
pub fn as_slice_of_cells(&self) -> &[GhostCell<'brand, T>] {
unsafe { &*(self.as_ptr() as *mut [GhostCell<'brand, T>]) }
}
pub fn from_slice_of_cells<'slice>(slice: &'slice [GhostCell<'brand, T>]) -> &'slice Self {
unsafe { &*(slice as *const _ as *const GhostCell<'brand, [T]>) }
}
}
impl<'brand, T, const N: usize> GhostCell<'brand, [T; N]> {
pub fn as_array_of_cells(&self) -> &[GhostCell<'brand, T>; N] {
unsafe { &*(self.as_ptr() as *mut [GhostCell<'brand, T>; N]) }
}
pub fn from_array_of_cells<'a>(array: &'a [GhostCell<'brand, T>; N]) -> &'a Self {
unsafe { &*(array as *const [GhostCell<'brand, T>; N] as *const Self) }
}
}
macro_rules! ghost_cell_transpose_tuple {
($($t:ident),*) => {
impl<'brand, $($t),*> GhostCell<'brand, ($($t,)*)> {
pub fn as_tuple_of_cells(&self) -> &($(GhostCell<'brand, $t>,)*) {
unsafe { &*(self.as_ptr() as *mut ($(GhostCell<'brand, $t>,)*)) }
}
#[allow(clippy::needless_lifetimes)]
pub fn from_tuple_of_cells<'a>(tuple: &'a ($(GhostCell<'brand, $t>,)*)) -> &'a Self {
unsafe { &*(tuple as *const ($(GhostCell<'brand, $t>,)*) as *const Self) }
}
}
}
}
ghost_cell_transpose_tuple!();
ghost_cell_transpose_tuple!(T0);
ghost_cell_transpose_tuple!(T0, T1);
ghost_cell_transpose_tuple!(T0, T1, T2);
ghost_cell_transpose_tuple!(T0, T1, T2, T3);
ghost_cell_transpose_tuple!(T0, T1, T2, T3, T4);
ghost_cell_transpose_tuple!(T0, T1, T2, T3, T4, T5);
ghost_cell_transpose_tuple!(T0, T1, T2, T3, T4, T5, T6);
ghost_cell_transpose_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
ghost_cell_transpose_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
ghost_cell_transpose_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
ghost_cell_transpose_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
ghost_cell_transpose_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl<'brand, T: ?Sized> AsMut<T> for GhostCell<'brand, T> {
fn as_mut(&mut self) -> &mut T {
self.get_mut()
}
}
impl<'brand, T> From<T> for GhostCell<'brand, T> {
fn from(t: T) -> Self {
Self::new(t)
}
}
unsafe impl<'brand, T: ?Sized + Send> Send for GhostCell<'brand, T> {}
unsafe impl<'brand, T: ?Sized + Send + Sync> Sync for GhostCell<'brand, T> {}
type InvariantLifetime<'brand> = PhantomData<fn(&'brand ()) -> &'brand ()>;
#[doc(hidden)]
pub mod compile_tests {
pub fn token_noescape() {}
pub fn cell_noescape() {}
pub fn cell_borrow_borrows_token() {}
pub fn cell_borrow_mut_borrows_token_mutably() {}
pub fn cell_borrow_borrows_cell() {}
pub fn cell_borrow_mut_borrows_cell() {}
pub fn cell_get_mut_borrows_cell_mutably() {}
pub fn cell_from_mut_borrows_value_mutably() {}
}