late_struct/
instance.rs

1use std::{
2    alloc,
3    any::TypeId,
4    cell::{self, Ref, RefCell, RefMut},
5    convert::Infallible,
6    fmt,
7    marker::PhantomData,
8    ptr::NonNull,
9};
10
11use scopeguard::ScopeGuard;
12
13use crate::{LateField, LateFieldDescriptor, LateLayoutInitToken, LateStruct};
14
15// === Exterior Mutability === //
16
17/// An instance of a [`LateStruct`].
18///
19/// You can define a late-initialized structure with the [`late_struct!`](crate::late_struct) macro
20/// and add fields to it with the [`late_field!`](crate::late_field) macro. You can then fetch these
21/// fields using the [`get`](LateInstance::get) and [`get_mut`](LateInstance::get_mut) methods.
22///
23/// These methods only allow users to borrow one field mutably at a time. If this is too
24/// restrictive, you can use the [`get_two`](LateInstance::get_two) method, which obtains two fields
25/// mutably at the same time. If that is still too restrictive, consider using the
26/// [`dynamic`](LateInstance::dynamic) method, which returns a [`LateInstanceDyn`] view of this
27/// structure providing a [`RefCell`]-like runtime-borrow-checked API for accessing fields. If that
28/// is still too restrictive, all fields in the structure exhibit
29/// [`UnsafeCell`](std::cell::UnsafeCell)-like semantics and thus can be borrowed manually using the
30/// [`get_ptr`](LateInstance::get_ptr) method.
31///
32/// ## Reflection Operations
33///
34/// Both [`LateInstance::fields`] and [`LateStruct::descriptor`] expose the set of fields in a given
35/// structure as a list of [`LateFieldDescriptor`]s. These can be used alongside the
36/// [`get_erased`](LateInstance::get_erased) and [`get_erased_mut`](LateInstance::get_erased_mut) to
37/// obtain references to the [`S::EraseTo`](LateStruct::EraseTo) types to which all fields are
38/// required to upcast.
39///
40/// ## Structural Traits
41///
42/// Trait implementations for this type are largely structural. If
43/// [`S::EraseTo`](LateStruct::EraseTo) implements [`Send`], for instance, this type will implement
44/// [`Send`]. Here is a full table of all of the structural trait implementations:
45///
46/// - `'static` and [`Default`], although since all fields are always required to implement these
47///   two traits, so too will [`LateInstance`].
48/// - [`Send`], [`Sync`], and [`Debug`](fmt::Debug) are all perfectly structural w.r.t `S::EraseTo`.
49/// - [`Eq`] is implemented if `S::EraseTo` implements [`DynEq`](super::DynEq).
50/// - [`PartialEq`] is implemented if `S::EraseTo` implements [`DynPartialEq`](super::DynPartialEq).
51/// - [`Clone`] is implemented if `S::EraseTo` implements [`DynClone`](super::DynClone).
52/// - [`Hash`] is implemented if `S::EraseTo` implements [`DynHash`](super::DynHash).
53///
54/// The [`DynEq`](super::DynEq), [`DynClone`](super::DynClone), and [`DynHash`](super::DynHash)
55/// traits exist because [`Eq`], [`Clone`], and [`Hash`], are not [`dyn` compatible](dyn-compat) and
56/// thus cannot directly be used in the bounds of a `dyn Trait` object.
57///
58/// We do not have a structural implementation for [`Ord`] since the order of fields inside this
59/// structure is not defined.
60///
61/// By default, `S::EraseTo` is set to `dyn 'static + fmt::Debug`. This implies that the
62/// `LateInstance` instantiating that `S` will implement [`Debug`](fmt::Debug) but will not
63/// implement, e.g., `Send` or `Sync`. See [this section](index.html#advanced-usage) of the crate
64/// level documentation for information on how to change these bounds.
65///
66/// [dyn-compat]: https://doc.rust-lang.org/1.87.0/reference/items/traits.html#r-items.traits.dyn-compatible
67pub struct LateInstance<S: LateStruct> {
68    _ty: PhantomData<fn(S) -> S>,
69
70    /// The ZST proving that we have initialized the late-bound layouts of all structures present in
71    /// this application.
72    init_token: LateLayoutInitToken,
73
74    /// The base pointer to the boxed structure.
75    data_base: NonNull<u8>,
76
77    /// Reference cells tracking borrows of fields of this structure when accessed through
78    /// [`LateInstanceDyn`]. Since `LateInstanceDyn` can only be constructed through a mutable
79    /// reference to this structure and `LateInstanceDyn` is `!Sync`, it is safe to contain these
80    /// objects in an otherwise `Sync` structure.
81    cells: Box<[RefCell<()>]>,
82}
83
84unsafe impl<S: LateStruct> Send for LateInstance<S> where
85    // `HashMap<TypeId, Box<S::EraseTo>>: Send` iff `S::EraseTo: Send`
86    S::EraseTo: Send
87{
88}
89
90unsafe impl<S: LateStruct> Sync for LateInstance<S> where
91    // `HashMap<TypeId, Box<S::EraseTo>>: Sync` iff `S::EraseTo: Sync`
92    S::EraseTo: Sync
93{
94}
95
96impl<S: LateStruct> fmt::Debug for LateInstance<S>
97where
98    S::EraseTo: fmt::Debug,
99{
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        let mut f = f.debug_struct("LateInstance");
102
103        for field in self.fields() {
104            f.field(field.key_type_name(), &self.get_erased_ptr(field));
105        }
106
107        f.finish()
108    }
109}
110
111impl<S: LateStruct> Default for LateInstance<S> {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117/// Regular operations.
118impl<S: LateStruct> LateInstance<S> {
119    /// Instantiate a new structure where each field is initialized using its implementation of the
120    /// [`Default`] trait.
121    ///
122    /// This is equivalent to the `LateInstance`'s implementation of `Default`.
123    pub fn new() -> Self {
124        unsafe {
125            Self::new_custom(|field, ptr| {
126                field.init(ptr);
127            })
128        }
129    }
130
131    /// Fetches an immutable reference to a field by its [`LateField`] marker type.
132    pub fn get<F: LateField<S>>(&self) -> &F::Value {
133        unsafe { self.get_ptr::<F>().as_ref() }
134    }
135
136    /// Fetches an mutable reference to a field by its [`LateField`] marker type.
137    ///
138    /// If you want to fetch more than one field at a time mutably, consider using the [`get_two`]
139    /// or [`dynamic`] methods.
140    ///
141    /// [`get_two`]: LateInstance::get_two
142    /// [`dynamic`]: LateInstance::dynamic
143    pub fn get_mut<F: LateField<S>>(&mut self) -> &mut F::Value {
144        unsafe { self.get_ptr::<F>().as_mut() }
145    }
146
147    /// Fetches a raw pointer to the structure's field by its [`LateField`] marker type. These
148    /// fields act as if they were wrapped inside an [`UnsafeCell`](std::cell::UnsafeCell) and thus
149    /// can be mutably dereferenced.
150    pub fn get_ptr<F: LateField<S>>(&self) -> NonNull<F::Value> {
151        unsafe {
152            self.data_base
153                .add(F::descriptor().offset(self.init_token))
154                .cast()
155        }
156    }
157
158    /// Fetches a pair of mutable references to distinct fields identified by their [`LateField`]
159    /// marker types. This method panics if `F` and `G` are the same type.
160    pub fn get_two<F, G>(&mut self) -> (&mut F::Value, &mut G::Value)
161    where
162        F: LateField<S>,
163        G: LateField<S>,
164    {
165        assert_ne!(TypeId::of::<F>(), TypeId::of::<G>());
166
167        unsafe { (self.get_ptr::<F>().as_mut(), self.get_ptr::<G>().as_mut()) }
168    }
169
170    /// Fetches a [`LateInstanceDyn`] view into the structure which exposes a [`RefCell`]-like API
171    /// for dynamically borrowing multiple fields mutably at the same time.
172    pub fn dynamic(&mut self) -> &mut LateInstanceDyn<S> {
173        unsafe { &mut *(self as *mut Self as *mut LateInstanceDyn<S>) }
174    }
175}
176
177/// Reflection operations.
178impl<S: LateStruct> LateInstance<S> {
179    /// Construct a new [`LateInstance`] using the `init` closure to initialize each field in the
180    /// order they appear in the
181    /// [`LateStructDescriptor::fields`](super::LateStructDescriptor::fields) list.
182    ///
183    /// ## Safety
184    ///
185    /// The pointee of each of the `*mut u8` pointers provided to `init` must be initialized to an
186    /// instance of the corresponding field's value type.
187    ///
188    pub unsafe fn new_custom(
189        mut init: impl FnMut(&'static LateFieldDescriptor<S>, *mut u8),
190    ) -> Self {
191        let Ok(res) = unsafe {
192            Self::try_new_custom(|field, ptr| {
193                init(field, ptr);
194                Result::<(), Infallible>::Ok(())
195            })
196        };
197        res
198    }
199
200    /// Construct a new [`LateInstance`] using the `init` closure to initialize each field in the
201    /// order they appear in the
202    /// [`LateStructDescriptor::fields`](super::LateStructDescriptor::fields) list. If `init`
203    /// returns an `Err`, the construction of the structure is aborted and the error is forwarded
204    /// to the caller.
205    ///
206    /// ## Safety
207    ///
208    /// The pointee of each of the `*mut u8` pointers provided to `init` must be initialized to an
209    /// instance of the corresponding field's value type.
210    ///
211    pub unsafe fn try_new_custom<E>(
212        mut init: impl FnMut(&'static LateFieldDescriptor<S>, *mut u8) -> Result<(), E>,
213    ) -> Result<Self, E> {
214        let init_token = LateLayoutInitToken::new();
215
216        let struct_layout = S::descriptor().layout(init_token);
217        let struct_fields = S::descriptor().fields(init_token);
218
219        // Allocate space for the instance
220        let Some(data_base) = NonNull::new(unsafe { alloc::alloc(struct_layout) }) else {
221            panic!("out of memory");
222        };
223
224        let dealloc_guard = scopeguard::guard((), |()| {
225            unsafe { alloc::dealloc(data_base.as_ptr(), struct_layout) };
226        });
227
228        // Initialize its fields
229        let mut drop_guard = scopeguard::guard(0usize, |cnt| {
230            for &field in &struct_fields[0..cnt] {
231                unsafe { field.drop(data_base.add(field.offset(init_token)).as_ptr()) };
232            }
233        });
234
235        for &field in struct_fields {
236            init(field, unsafe {
237                data_base.add(field.offset(init_token)).as_ptr()
238            })?;
239            *drop_guard += 1;
240        }
241
242        // Defuse guards
243        ScopeGuard::into_inner(dealloc_guard);
244        ScopeGuard::into_inner(drop_guard);
245
246        Ok(Self {
247            _ty: PhantomData,
248            init_token,
249            data_base,
250            cells: Box::from_iter((0..struct_fields.len()).map(|_| RefCell::new(()))),
251        })
252    }
253
254    /// Fetches a [`LateLayoutInitToken`] attesting to the fact that all late-initialized structure
255    /// layouts and field offsets have been resolved.
256    pub fn init_token(&self) -> LateLayoutInitToken {
257        self.init_token
258    }
259
260    /// Fetches the set of fields comprising the structure. This is equivalent to calling the
261    /// [`fields`](crate::LateStructDescriptor::fields) method on the
262    /// [`LateStructDescriptor`](crate::LateStructDescriptor) instance returned by
263    /// [`S::descriptor()`](LateStruct::descriptor).
264    pub fn fields(&self) -> &'static [&'static LateFieldDescriptor<S>] {
265        S::descriptor().fields(self.init_token)
266    }
267
268    /// Fetches the pointer to the base of the heap allocation containing the structure's values.
269    pub fn base_ptr(&self) -> NonNull<u8> {
270        self.data_base
271    }
272
273    /// Fetches an immutable reference to a field by its [`LateFieldDescriptor`] instance.
274    pub fn get_erased(&self, field: &LateFieldDescriptor<S>) -> &S::EraseTo {
275        unsafe { self.get_erased_ptr(field).as_ref() }
276    }
277
278    /// Fetches a mutable reference to a field by its [`LateFieldDescriptor`] instance.
279    ///
280    /// If you want to fetch more than one field at a time mutably, consider using the [`get_two`]
281    /// or [`dynamic`] methods.
282    ///
283    /// [`get_two`]: LateInstance::get_two
284    /// [`dynamic`]: LateInstance::dynamic
285    pub fn get_erased_mut(&mut self, field: &LateFieldDescriptor<S>) -> &mut S::EraseTo {
286        unsafe { self.get_erased_ptr(field).as_mut() }
287    }
288
289    /// Fetches a raw pointer to the structure's field identified by its [`LateFieldDescriptor`]
290    /// instance. These fields act as if they were wrapped inside an
291    /// [`UnsafeCell`](std::cell::UnsafeCell) and thus can be mutably dereferenced.
292    pub fn get_erased_ptr(&self, field: &LateFieldDescriptor<S>) -> NonNull<S::EraseTo> {
293        unsafe {
294            NonNull::new_unchecked(
295                field.erase_value(
296                    self.data_base
297                        .add(field.offset(self.init_token))
298                        .cast()
299                        .as_ptr(),
300                ),
301            )
302        }
303    }
304}
305
306impl<S: LateStruct> Drop for LateInstance<S> {
307    fn drop(&mut self) {
308        let struct_layout = S::descriptor().layout(self.init_token);
309        let struct_fields = S::descriptor().fields(self.init_token);
310
311        for field in struct_fields {
312            unsafe { field.drop(self.data_base.add(field.offset(self.init_token)).as_ptr()) }
313        }
314
315        unsafe { alloc::dealloc(self.data_base.as_ptr(), struct_layout) };
316    }
317}
318
319// === Interior Mutability === //
320
321/// A view around a [`LateInstance`] which exposes a [`RefCell`]-like API for dynamically borrowing
322/// multiple fields mutably at the same time.
323#[repr(transparent)]
324pub struct LateInstanceDyn<S: LateStruct> {
325    _not_send_sync: PhantomData<*const ()>,
326    inner: LateInstance<S>,
327}
328
329impl<S: LateStruct> fmt::Debug for LateInstanceDyn<S>
330where
331    S::EraseTo: fmt::Debug,
332{
333    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334        let mut f = f.debug_struct("LateInstanceDynamic");
335
336        for field in self.fields() {
337            f.field(field.key_type_name(), &self.try_borrow_erased(field));
338        }
339
340        f.finish()
341    }
342}
343
344impl<S: LateStruct> LateInstanceDyn<S> {
345    /// Obtains a reference to the underlying [`LateInstance`].
346    pub fn non_dynamic(&mut self) -> &mut LateInstance<S> {
347        &mut self.inner
348    }
349
350    /// Forwards to [`LateInstance::init_token`].
351    pub fn init_token(&self) -> LateLayoutInitToken {
352        self.inner.init_token
353    }
354
355    /// Forwards to [`LateInstance::fields`].
356    pub fn fields(&self) -> &'static [&'static LateFieldDescriptor<S>] {
357        self.inner.fields()
358    }
359
360    /// Forwards to [`LateInstance::get_ptr`].
361    pub fn get_ptr<F: LateField<S>>(&self) -> NonNull<F::Value> {
362        self.inner.get_ptr::<F>()
363    }
364
365    /// Forwards to [`LateInstance::get_erased_ptr`].
366    pub fn get_erased_ptr(&self, field: &LateFieldDescriptor<S>) -> NonNull<S::EraseTo> {
367        self.inner.get_erased_ptr(field)
368    }
369
370    /// Forwards to [`LateInstance::get_mut`].
371    pub fn get_mut<F: LateField<S>>(&mut self) -> &mut F::Value {
372        self.inner.get_mut::<F>()
373    }
374
375    /// Borrow the field identified by the supplied [`LateField`] marker type immutably, panicking
376    /// if the borrow failed.
377    ///
378    /// The smart-pointers returned by this method are compatible with those returned by
379    /// [`RefCell`].
380    pub fn borrow<F: LateField<S>>(&self) -> Ref<'_, F::Value> {
381        Ref::map(
382            self.inner.cells[F::descriptor().index(self.inner.init_token)].borrow(),
383            |()| unsafe { self.get_ptr::<F>().as_ref() },
384        )
385    }
386
387    /// Borrow the field identified by the supplied [`LateField`] marker type mutably, panicking
388    /// if the borrow failed.
389    ///
390    /// The smart-pointers returned by this method are compatible with those returned by
391    /// [`RefCell`].
392    pub fn borrow_mut<F: LateField<S>>(&self) -> RefMut<'_, F::Value> {
393        RefMut::map(
394            self.inner.cells[F::descriptor().index(self.inner.init_token)].borrow_mut(),
395            |()| unsafe { self.get_ptr::<F>().as_mut() },
396        )
397    }
398
399    /// Borrow the field identified by the supplied [`LateField`] marker type immutably or return a
400    /// [`cell::BorrowError`] if the borrow failed.
401    ///
402    /// The smart-pointers and errors returned by this method are compatible with those returned by
403    /// [`RefCell`].
404    pub fn try_borrow<F: LateField<S>>(&self) -> Result<Ref<'_, F::Value>, cell::BorrowError> {
405        self.inner.cells[F::descriptor().index(self.inner.init_token)]
406            .try_borrow()
407            .map(|field| Ref::map(field, |()| unsafe { self.get_ptr::<F>().as_ref() }))
408    }
409
410    /// Borrow the field identified by the supplied [`LateField`] marker type mutably or return a
411    /// [`cell::BorrowError`] if the borrow failed.
412    ///
413    /// The smart-pointers and errors returned by this method are compatible with those returned by
414    /// [`RefCell`].
415    pub fn try_borrow_mut<F: LateField<S>>(
416        &self,
417    ) -> Result<RefMut<'_, F::Value>, cell::BorrowMutError> {
418        self.inner.cells[F::descriptor().index(self.inner.init_token)]
419            .try_borrow_mut()
420            .map(|field| RefMut::map(field, |()| unsafe { self.get_ptr::<F>().as_mut() }))
421    }
422
423    /// Borrow the field identified by the supplied [`LateFieldDescriptor`] immutably, panicking
424    /// if the borrow failed.
425    ///
426    /// The smart-pointers returned by this method are compatible with those returned by
427    /// [`RefCell`].
428    pub fn borrow_erased<'a>(&'a self, field: &LateFieldDescriptor<S>) -> Ref<'a, S::EraseTo> {
429        Ref::map(
430            self.inner.cells[field.index(self.inner.init_token)].borrow(),
431            |()| unsafe { self.get_erased_ptr(field).as_ref() },
432        )
433    }
434
435    /// Borrow the field identified by the supplied [`LateFieldDescriptor`] mutably, panicking
436    /// if the borrow failed.
437    ///
438    /// The smart-pointers returned by this method are compatible with those returned by
439    /// [`RefCell`].
440    pub fn borrow_erased_mut<'a>(
441        &'a self,
442        field: &LateFieldDescriptor<S>,
443    ) -> RefMut<'a, S::EraseTo> {
444        RefMut::map(
445            self.inner.cells[field.index(self.inner.init_token)].borrow_mut(),
446            |()| unsafe { self.get_erased_ptr(field).as_mut() },
447        )
448    }
449
450    /// Borrow the field identified by the supplied [`LateFieldDescriptor`] immutably or return a
451    /// [`cell::BorrowError`] if the borrow failed.
452    ///
453    /// The smart-pointers and errors returned by this method are compatible with those returned by
454    /// [`RefCell`].
455    pub fn try_borrow_erased<'a>(
456        &'a self,
457        field: &LateFieldDescriptor<S>,
458    ) -> Result<Ref<'a, S::EraseTo>, cell::BorrowError> {
459        self.inner.cells[field.index(self.inner.init_token)]
460            .try_borrow()
461            .map(|field_guard| {
462                Ref::map(field_guard, |()| unsafe {
463                    self.get_erased_ptr(field).as_ref()
464                })
465            })
466    }
467
468    /// Borrow the field identified by the supplied [`LateFieldDescriptor`] mutably or return a
469    /// [`cell::BorrowMutError`] if the borrow failed.
470    ///
471    /// The smart-pointers and errors returned by this method are compatible with those returned by
472    /// [`RefCell`].
473    pub fn try_borrow_erased_mut<'a>(
474        &'a self,
475        field: &LateFieldDescriptor<S>,
476    ) -> Result<RefMut<'a, S::EraseTo>, cell::BorrowMutError> {
477        self.inner.cells[field.index(self.inner.init_token)]
478            .try_borrow_mut()
479            .map(|field_guard| {
480                RefMut::map(field_guard, |()| unsafe {
481                    self.get_erased_ptr(field).as_mut()
482                })
483            })
484    }
485}