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}