#![no_std]
#[doc(hidden)]
pub extern crate alloc;
#[doc(hidden)]
pub mod unsafe_self_cell;
#[macro_export]
macro_rules! self_cell {
(
$(#[$StructMeta:meta])*
$Vis:vis struct $StructName:ident $(<$OwnerLifetime:lifetime>)? {
owner: $Owner:ty,
#[$Covariance:ident]
dependent: $Dependent:ident,
}
$(impl {$($AutomaticDerive:ident),*})?
) => {
#[repr(transparent)]
$(#[$StructMeta])*
$Vis struct $StructName $(<$OwnerLifetime>)? {
unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell<
$StructName$(<$OwnerLifetime>)?,
$Owner,
$Dependent<'static>
>,
$(owner_marker: $crate::_covariant_owner_marker!($Covariance, $OwnerLifetime) ,)?
}
impl $(<$OwnerLifetime>)? $StructName $(<$OwnerLifetime>)? {
$Vis fn new(
owner: $Owner,
dependent_builder: impl for<'_q> FnOnce(&'_q $Owner) -> $Dependent<'_q>
) -> Self {
use core::ptr::NonNull;
unsafe {
type JoinedCell<'_q $(, $OwnerLifetime)?> =
$crate::unsafe_self_cell::JoinedCell<$Owner, $Dependent<'_q>>;
let layout = $crate::alloc::alloc::Layout::new::<JoinedCell>();
assert!(layout.size() != 0);
let joined_void_ptr = NonNull::new($crate::alloc::alloc::alloc(layout)).unwrap();
let mut joined_ptr = core::mem::transmute::<NonNull<u8>, NonNull<JoinedCell>>(
joined_void_ptr
);
let (owner_ptr, dependent_ptr) = JoinedCell::_field_pointers(joined_ptr.as_ptr());
owner_ptr.write(owner);
let drop_guard =
$crate::unsafe_self_cell::OwnerAndCellDropGuard::new(joined_ptr);
dependent_ptr.write(dependent_builder(&*owner_ptr));
core::mem::forget(drop_guard);
Self {
unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new(
joined_void_ptr,
),
$(owner_marker: $crate::_covariant_owner_marker_ctor!($OwnerLifetime) ,)?
}
}
}
$Vis fn try_new<Err>(
owner: $Owner,
dependent_builder:
impl for<'_q> FnOnce(&'_q $Owner) -> core::result::Result<$Dependent<'_q>, Err>
) -> core::result::Result<Self, Err> {
use core::ptr::NonNull;
unsafe {
type JoinedCell<'_q $(, $OwnerLifetime)?> =
$crate::unsafe_self_cell::JoinedCell<$Owner, $Dependent<'_q>>;
let layout = $crate::alloc::alloc::Layout::new::<JoinedCell>();
assert!(layout.size() != 0);
let joined_void_ptr = NonNull::new($crate::alloc::alloc::alloc(layout)).unwrap();
let mut joined_ptr = core::mem::transmute::<NonNull<u8>, NonNull<JoinedCell>>(
joined_void_ptr
);
let (owner_ptr, dependent_ptr) = JoinedCell::_field_pointers(joined_ptr.as_ptr());
owner_ptr.write(owner);
let mut drop_guard =
$crate::unsafe_self_cell::OwnerAndCellDropGuard::new(joined_ptr);
match dependent_builder(&*owner_ptr) {
Ok(dependent) => {
dependent_ptr.write(dependent);
core::mem::forget(drop_guard);
Ok(Self {
unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new(
joined_void_ptr,
),
$(owner_marker: $crate::_covariant_owner_marker_ctor!($OwnerLifetime) ,)?
})
}
Err(err) => Err(err)
}
}
}
$Vis fn try_new_or_recover<Err>(
owner: $Owner,
dependent_builder:
impl for<'_q> FnOnce(&'_q $Owner) -> core::result::Result<$Dependent<'_q>, Err>
) -> core::result::Result<Self, ($Owner, Err)> {
use core::ptr::NonNull;
unsafe {
type JoinedCell<'_q $(, $OwnerLifetime)?> =
$crate::unsafe_self_cell::JoinedCell<$Owner, $Dependent<'_q>>;
let layout = $crate::alloc::alloc::Layout::new::<JoinedCell>();
assert!(layout.size() != 0);
let joined_void_ptr = NonNull::new($crate::alloc::alloc::alloc(layout)).unwrap();
let mut joined_ptr = core::mem::transmute::<NonNull<u8>, NonNull<JoinedCell>>(
joined_void_ptr
);
let (owner_ptr, dependent_ptr) = JoinedCell::_field_pointers(joined_ptr.as_ptr());
owner_ptr.write(owner);
let mut drop_guard =
$crate::unsafe_self_cell::OwnerAndCellDropGuard::new(joined_ptr);
match dependent_builder(&*owner_ptr) {
Ok(dependent) => {
dependent_ptr.write(dependent);
core::mem::forget(drop_guard);
Ok(Self {
unsafe_self_cell: $crate::unsafe_self_cell::UnsafeSelfCell::new(
joined_void_ptr,
),
$(owner_marker: $crate::_covariant_owner_marker_ctor!($OwnerLifetime) ,)?
})
}
Err(err) => {
let owner_on_err = core::ptr::read(owner_ptr);
core::mem::forget(drop_guard);
$crate::alloc::alloc::dealloc(joined_void_ptr.as_ptr(), layout);
Err((owner_on_err, err))
}
}
}
}
$Vis fn borrow_owner<'_q>(&'_q self) -> &'_q $Owner {
unsafe { self.unsafe_self_cell.borrow_owner::<$Dependent<'_q>>() }
}
$Vis fn with_dependent<'outer_fn, Ret>(
&'outer_fn self,
func: impl for<'_q> FnOnce(&'_q $Owner, &'outer_fn $Dependent<'_q>
) -> Ret) -> Ret {
unsafe {
func(
self.unsafe_self_cell.borrow_owner::<$Dependent>(),
self.unsafe_self_cell.borrow_dependent()
)
}
}
$Vis fn with_dependent_mut<'outer_fn, Ret>(
&'outer_fn mut self,
func: impl for<'_q> FnOnce(&'_q $Owner, &'outer_fn mut $Dependent<'_q>) -> Ret
) -> Ret {
let (owner, dependent) = unsafe {
self.unsafe_self_cell.borrow_mut()
};
func(owner, dependent)
}
$crate::_covariant_access!($Covariance, $Vis, $Dependent);
$Vis fn into_owner(self) -> $Owner {
let unsafe_self_cell = unsafe { core::mem::transmute::<
Self,
$crate::unsafe_self_cell::UnsafeSelfCell<
$StructName$(<$OwnerLifetime>)?,
$Owner,
$Dependent<'static>
>
>(self) };
let owner = unsafe { unsafe_self_cell.into_owner::<$Dependent>() };
owner
}
}
impl $(<$OwnerLifetime>)? Drop for $StructName $(<$OwnerLifetime>)? {
fn drop(&mut self) {
unsafe {
self.unsafe_self_cell.drop_joined::<$Dependent>();
}
}
}
$($(
$crate::_impl_automatic_derive!($AutomaticDerive, $StructName);
)*)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _covariant_access {
(covariant, $Vis:vis, $Dependent:ident) => {
$Vis fn borrow_dependent<'_q>(&'_q self) -> &'_q $Dependent<'_q> {
fn _assert_covariance<'x: 'y, 'y>(x: $Dependent<'x>) -> $Dependent<'y> {
x }
unsafe { self.unsafe_self_cell.borrow_dependent() }
}
};
(not_covariant, $Vis:vis, $Dependent:ident) => {
};
($x:ident, $Vis:vis, $Dependent:ident) => {
compile_error!("This macro only accepts `covariant` or `not_covariant`");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _covariant_owner_marker {
(covariant, $OwnerLifetime:lifetime) => {
core::marker::PhantomData<&$OwnerLifetime ()>
};
(not_covariant, $OwnerLifetime:lifetime) => {
core::marker::PhantomData<fn(&$OwnerLifetime ()) -> &$OwnerLifetime ()>
};
($x:ident, $OwnerLifetime:lifetime) => {
compile_error!("This macro only accepts `covariant` or `not_covariant`");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _covariant_owner_marker_ctor {
($OwnerLifetime:lifetime) => {
core::marker::PhantomData
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _impl_automatic_derive {
(Debug, $StructName:ident) => {
impl core::fmt::Debug for $StructName {
fn fmt(
&self,
fmt: &mut core::fmt::Formatter,
) -> core::result::Result<(), core::fmt::Error> {
self.with_dependent(|owner, dependent| {
fmt.debug_struct(stringify!($StructName))
.field("owner", owner)
.field("dependent", dependent)
.finish()
})
}
}
};
(PartialEq, $StructName:ident) => {
impl core::cmp::PartialEq for $StructName {
fn eq(&self, other: &Self) -> bool {
*self.borrow_owner() == *other.borrow_owner()
}
}
};
(Eq, $StructName:ident) => {
impl core::cmp::Eq for $StructName {}
};
(Hash, $StructName:ident) => {
impl core::hash::Hash for $StructName {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.borrow_owner().hash(state);
}
}
};
($x:ident, $StructName:ident) => {
compile_error!(concat!(
"No automatic trait impl for trait: ",
stringify!($x)
));
};
}