self_cell/
unsafe_self_cell.rs

1#![allow(clippy::missing_safety_doc, clippy::needless_lifetimes)]
2
3use core::cell::UnsafeCell;
4use core::marker::PhantomData;
5use core::mem;
6use core::ptr::{drop_in_place, read, NonNull};
7use core::sync::atomic::{AtomicBool, Ordering};
8
9extern crate alloc;
10
11use alloc::alloc::{dealloc, Layout};
12
13// Self referential structs are currently not supported with safe vanilla Rust.
14// The only reasonable safe alternative is to expect the user to juggle 2 separate
15// data structures which is a mess. The library solution rental is both no longer
16// maintained and really heavy to compile. So begrudgingly I rolled my own version.
17// These are some of the core invariants we require for this to be safe to use.
18//
19// 1. owner is initialized when UnsafeSelfCell is constructed.
20// 2. owner is NEVER changed again.
21// 3. The pointer to owner and dependent never changes, even when moved.
22// 4. The only access to owner and dependent is as immutable reference.
23// 5. owner lives longer than dependent.
24
25#[doc(hidden)]
26pub struct JoinedCell<Owner, Dependent> {
27    pub owner: Owner,
28    pub dependent: Dependent,
29}
30
31// Library controlled struct that marks all accesses as unsafe.
32// Because the macro generated struct impl can be extended, could be unsafe.
33#[doc(hidden)]
34pub struct UnsafeSelfCell<ContainedIn, Owner, DependentStatic: 'static> {
35    joined_void_ptr: NonNull<u8>,
36
37    // ContainedIn is necessary for type safety since we don't fully
38    // prohibit access to the UnsafeSelfCell; swapping between different
39    // structs can be unsafe otherwise, see Issue #17.
40    contained_in_marker: PhantomData<ContainedIn>,
41
42    owner_marker: PhantomData<Owner>,
43    // DependentStatic is only used to correctly derive Send and Sync.
44    dependent_marker: PhantomData<DependentStatic>,
45}
46
47impl<ContainedIn, Owner, DependentStatic> UnsafeSelfCell<ContainedIn, Owner, DependentStatic> {
48    pub unsafe fn new(joined_void_ptr: NonNull<u8>) -> Self {
49        Self {
50            joined_void_ptr,
51            contained_in_marker: PhantomData,
52            owner_marker: PhantomData,
53            dependent_marker: PhantomData,
54        }
55    }
56
57    // Calling any of these *unsafe* functions with the wrong Dependent type is UB.
58
59    pub unsafe fn borrow_owner<'a, Dependent>(&'a self) -> &'a Owner {
60        let joined_ptr = self.joined_void_ptr.cast::<JoinedCell<Owner, Dependent>>();
61
62        &(*joined_ptr.as_ptr()).owner
63    }
64
65    pub unsafe fn borrow_dependent<'a, Dependent>(&'a self) -> &'a Dependent {
66        let joined_ptr = self.joined_void_ptr.cast::<JoinedCell<Owner, Dependent>>();
67
68        &(*joined_ptr.as_ptr()).dependent
69    }
70
71    pub unsafe fn borrow_mut<'a, Dependent>(&'a mut self) -> (&'a Owner, &'a mut Dependent) {
72        let joined_ptr = self.joined_void_ptr.cast::<JoinedCell<Owner, Dependent>>();
73
74        // This function used to return `&'a mut JoinedCell<Owner, Dependent>`.
75        // It now creates two references to the fields instead to avoid claiming mutable access
76        // to the whole `JoinedCell` (including the owner!) here.
77        (
78            &(*joined_ptr.as_ptr()).owner,
79            &mut (*joined_ptr.as_ptr()).dependent,
80        )
81    }
82
83    // Any subsequent use of this struct other than dropping it is UB.
84    pub unsafe fn drop_joined<Dependent>(&mut self) {
85        let joined_ptr = self.joined_void_ptr.cast::<JoinedCell<Owner, Dependent>>();
86
87        // Also used in case drop_in_place(...dependent) fails
88        let _guard = OwnerAndCellDropGuard { joined_ptr };
89
90        // IMPORTANT dependent must be dropped before owner.
91        // We don't want to rely on an implicit order of struct fields.
92        // So we drop the struct, field by field manually.
93        drop_in_place(&mut (*joined_ptr.as_ptr()).dependent);
94
95        // Dropping owner
96        // and deallocating
97        // due to _guard at end of scope.
98    }
99
100    pub unsafe fn into_owner<Dependent>(self) -> Owner {
101        let joined_ptr = self.joined_void_ptr.cast::<JoinedCell<Owner, Dependent>>();
102
103        // In case drop_in_place(...dependent) fails
104        let drop_guard = OwnerAndCellDropGuard::new(joined_ptr);
105
106        // Drop dependent
107        drop_in_place(&mut (*joined_ptr.as_ptr()).dependent);
108
109        mem::forget(drop_guard);
110
111        let owner_ptr: *const Owner = &(*joined_ptr.as_ptr()).owner;
112
113        // Move owner out so it can be returned.
114        // Must not read before dropping dependent!! (Which happened above.)
115        let owner = read(owner_ptr);
116
117        // Deallocate JoinedCell
118        let layout = Layout::new::<JoinedCell<Owner, Dependent>>();
119        dealloc(self.joined_void_ptr.as_ptr(), layout);
120
121        owner
122    }
123}
124
125unsafe impl<ContainedIn, Owner, DependentStatic> Send
126    for UnsafeSelfCell<ContainedIn, Owner, DependentStatic>
127where
128    // Only derive Send if Owner and DependentStatic is also Send
129    Owner: Send,
130    DependentStatic: Send,
131{
132}
133
134unsafe impl<ContainedIn, Owner, DependentStatic> Sync
135    for UnsafeSelfCell<ContainedIn, Owner, DependentStatic>
136where
137    // Only derive Sync if Owner and DependentStatic is also Sync
138    Owner: Sync,
139    DependentStatic: Sync,
140{
141}
142
143// This struct is used to safely deallocate only the owner if dependent
144// construction fails.
145//
146// mem::forget it once it's no longer needed or dtor will be UB.
147#[doc(hidden)]
148pub struct OwnerAndCellDropGuard<Owner, Dependent> {
149    joined_ptr: NonNull<JoinedCell<Owner, Dependent>>,
150}
151
152impl<Owner, Dependent> OwnerAndCellDropGuard<Owner, Dependent> {
153    pub unsafe fn new(joined_ptr: NonNull<JoinedCell<Owner, Dependent>>) -> Self {
154        Self { joined_ptr }
155    }
156}
157
158impl<Owner, Dependent> Drop for OwnerAndCellDropGuard<Owner, Dependent> {
159    fn drop(&mut self) {
160        struct DeallocGuard {
161            ptr: *mut u8,
162            layout: Layout,
163        }
164        impl Drop for DeallocGuard {
165            fn drop(&mut self) {
166                unsafe { dealloc(self.ptr, self.layout) }
167            }
168        }
169
170        // Deallocate even when the drop_in_place(...owner) panics
171        let _guard = DeallocGuard {
172            ptr: self.joined_ptr.as_ptr() as *mut u8,
173            layout: Layout::new::<JoinedCell<Owner, Dependent>>(),
174        };
175
176        unsafe {
177            // We must only drop owner and the struct itself,
178            // The whole point of this drop guard is to clean up the partially
179            // initialized struct should building the dependent fail.
180            drop_in_place(&mut (*self.joined_ptr.as_ptr()).owner);
181        }
182
183        // Deallocation happens at end of scope
184    }
185}
186
187// Older versions of rust do not support addr_of_mut!. What we want to do here
188// is to emulate the behavior of that macro by going (incorrectly) via a
189// reference cast. Technically this is UB, but testing does not show the older
190// compiler versions (ab)using this. For discussions about this behavior see
191// https://github.com/Voultapher/self_cell/pull/31 and
192// https://github.com/Voultapher/self_cell/issues/30 and
193// https://github.com/Voultapher/self_cell/pull/33
194//
195// Because of 'procedural macros cannot expand to macro definitions'
196// we have wrap this in functions.
197impl<Owner, Dependent> JoinedCell<Owner, Dependent> {
198    #[doc(hidden)]
199    #[cfg(not(feature = "old_rust"))]
200    pub unsafe fn _field_pointers(this: *mut Self) -> (*mut Owner, *mut Dependent) {
201        let owner_ptr = core::ptr::addr_of_mut!((*this).owner);
202        let dependent_ptr = core::ptr::addr_of_mut!((*this).dependent);
203
204        (owner_ptr, dependent_ptr)
205    }
206
207    #[doc(hidden)]
208    #[cfg(feature = "old_rust")]
209    #[rustversion::since(1.51)]
210    pub unsafe fn _field_pointers(this: *mut Self) -> (*mut Owner, *mut Dependent) {
211        let owner_ptr = core::ptr::addr_of_mut!((*this).owner);
212        let dependent_ptr = core::ptr::addr_of_mut!((*this).dependent);
213
214        (owner_ptr, dependent_ptr)
215    }
216
217    #[doc(hidden)]
218    #[cfg(feature = "old_rust")]
219    #[rustversion::before(1.51)]
220    pub unsafe fn _field_pointers(this: *mut Self) -> (*mut Owner, *mut Dependent) {
221        // See comment above, technically this is UB.
222        let owner_ptr = &mut (*this).owner as *mut Owner;
223        let dependent_ptr = &mut (*this).dependent as *mut Dependent;
224
225        (owner_ptr, dependent_ptr)
226    }
227}
228
229/// Wrapper type that allows creating a self-referential type that hold a mutable borrow `&mut T`.
230///
231/// Example usage:
232///
233/// ```
234/// use self_cell::{self_cell, MutBorrow};
235///
236/// type MutStringRef<'a> = &'a mut String;
237///
238/// self_cell!(
239///     struct MutStringCell {
240///         owner: MutBorrow<String>,
241///
242///         #[covariant]
243///         dependent: MutStringRef,
244///     }
245/// );
246///
247/// let mut cell = MutStringCell::new(MutBorrow::new("abc".into()), |owner| owner.borrow_mut());
248/// cell.with_dependent_mut(|_owner, dependent| {
249///     assert_eq!(dependent, &"abc");
250///     dependent.pop();
251///     assert_eq!(dependent, &"ab");
252/// });
253///
254/// let recovered_owner: String = cell.into_owner().into_inner();
255/// assert_eq!(recovered_owner, "ab");
256/// ```
257pub struct MutBorrow<T> {
258    // Private on purpose.
259    is_locked: AtomicBool,
260    value: UnsafeCell<T>,
261}
262
263impl<T> MutBorrow<T> {
264    /// Constructs a new `MutBorrow`.
265    pub fn new(value: T) -> Self {
266        // Use the Rust type system to model an affine type that can only go from unlocked -> locked
267        // but never the other way around.
268        Self {
269            is_locked: AtomicBool::new(false),
270            value: UnsafeCell::new(value),
271        }
272    }
273
274    /// Obtains a mutable reference to the underlying data.
275    ///
276    /// This function can only sensibly be used in the builder function. Afterwards, it's impossible
277    /// to access the inner value, with the exception of [`MutBorrow::into_inner`].
278    ///
279    /// # Panics
280    ///
281    /// Will panic if called anywhere but in the dependent constructor. Will also panic if called
282    /// more than once.
283    #[allow(clippy::mut_from_ref)]
284    pub fn borrow_mut(&self) -> &mut T {
285        // Ensure this function can only be called once.
286        // Relaxed should be fine, because only one thread could ever read `false` anyway,
287        // so further synchronization is pointless.
288        let was_locked = self.is_locked.swap(true, Ordering::Relaxed);
289
290        if was_locked {
291            panic!("Tried to access locked MutBorrow")
292        } else {
293            // SAFETY: `self.is_locked` starts out as locked and can never be unlocked again, which
294            // guarantees that this function can only be called once. And the `self.value` being
295            // private ensures that there are no other references to it.
296            unsafe { &mut *self.value.get() }
297        }
298    }
299
300    /// Consumes `self` and returns the wrapped value.
301    pub fn into_inner(self) -> T {
302        self.value.into_inner()
303    }
304}
305
306// SAFETY: The reasoning why it is safe to share `MutBorrow` across threads is as follows: The
307// `AtomicBool` `is_locked` ensures that only ever exactly one thread can get access to the inner
308// value. In that sense it works like a critical section, that begins when `borrow_mut()` is called
309// and that ends when the outer `MutBorrow` is dropped. Once one thread acquired the unique
310// reference through `borrow_mut()` no other interaction with the inner value MUST ever be possible
311// while the outer `MutBorrow` is alive.
312unsafe impl<T: Send> Sync for MutBorrow<T> {}