#![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<
$Owner,
$Dependent<'static>
>
}
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: *mut $Owner = &mut (*joined_ptr.as_ptr()).owner;
let dependent_ptr: *mut $Dependent = &mut (*joined_ptr.as_ptr()).dependent;
owner_ptr.write(owner);
let mut 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,
),
}
}
}
$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: *mut $Owner = &mut (*joined_ptr.as_ptr()).owner;
let dependent_ptr: *mut $Dependent = &mut (*joined_ptr.as_ptr()).dependent;
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,
),
})
}
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: *mut $Owner = &mut (*joined_ptr.as_ptr()).owner;
let dependent_ptr: *mut $Dependent = &mut (*joined_ptr.as_ptr()).dependent;
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,
),
})
}
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<Ret>(&self, func: impl for<'_q> FnOnce(&'_q $Owner, &'_q $Dependent<'_q>) -> Ret) -> Ret {
unsafe {
func(
self.unsafe_self_cell.borrow_owner::<$Dependent>(),
self.unsafe_self_cell.borrow_dependent()
)
}
}
$Vis fn with_dependent_mut<Ret>(&mut self, func: impl for<'_q> FnOnce(&'_q $Owner, &'_q mut $Dependent<'_q>) -> Ret) -> Ret {
let joined_cell = unsafe {
self.unsafe_self_cell.borrow_mut()
};
func(&joined_cell.owner, &mut joined_cell.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<
$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! _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| {
write!(
fmt,
concat!(
stringify!($StructName),
" {{ owner: {:?}, dependent: {:?} }}"
),
owner, dependent
)
})
}
}
};
(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)
));
};
}