late_struct/
descriptor.rs

1use std::{
2    alloc::Layout,
3    any::{TypeId, type_name},
4    marker::PhantomData,
5    mem::MaybeUninit,
6    ptr, slice,
7    sync::atomic::{AtomicPtr, AtomicUsize, Ordering::*},
8};
9
10use crate::{LateField, LateLayoutInitToken, LateStruct};
11
12// === Raw === //
13
14/// A raw (untyped) descriptor for a [`LateStruct`].
15///
16/// You can obtain a reference to this descriptor using [`LateStruct::raw_descriptor`].
17///
18/// Unlike [`LateStructDescriptor`], the type of the `LateStruct` to which this descriptor belongs
19/// is erased.
20#[derive(Debug)]
21pub struct RawLateStructDescriptor {
22    // These need to be accessed in `init`.
23    pub(crate) size: AtomicUsize,
24    pub(crate) align: AtomicUsize,
25    pub(crate) fields: AtomicPtr<&'static [&'static RawLateFieldDescriptor]>,
26
27    type_name: fn() -> &'static str,
28    type_id: fn() -> TypeId,
29}
30
31impl RawLateStructDescriptor {
32    // This needs to be accessed in `traits`
33    pub(crate) const fn new<S: LateStruct>() -> Self {
34        Self {
35            size: AtomicUsize::new(0),
36            align: AtomicUsize::new(0),
37            fields: AtomicPtr::new(ptr::null_mut()),
38            type_name: type_name::<S>,
39            type_id: TypeId::of::<S>,
40        }
41    }
42
43    /// Fetches the name of the type for which [`LateStruct`] is implemented.
44    pub fn type_name(&self) -> &'static str {
45        (self.type_name)()
46    }
47
48    /// Fetches the [`TypeId`] of the type for which [`LateStruct`] is implemented.
49    pub fn type_id(&self) -> TypeId {
50        (self.type_id)()
51    }
52
53    /// Fetches the overall layout of the structure generated dynamically for this structure.
54    pub fn layout(&self, token: LateLayoutInitToken) -> Layout {
55        let _ = token;
56
57        unsafe {
58            Layout::from_size_align_unchecked(self.size.load(Relaxed), self.align.load(Relaxed))
59        }
60    }
61
62    /// Fetches the list of fields contained within this structure.
63    ///
64    /// The order of this list is in no way stable across multiple compilations.
65    pub fn fields(&self, token: LateLayoutInitToken) -> &'static [&'static RawLateFieldDescriptor] {
66        let _ = token;
67
68        unsafe { &**self.fields.load(Relaxed) }
69    }
70
71    /// Downcasts the descriptor to a typed [`LateStructDescriptor`] instance.
72    ///
73    /// `S` must match the type of the [`LateStruct`] marker this descriptor is describing or the
74    /// method will panic.
75    pub fn typed<S: LateStruct>(&self) -> &LateStructDescriptor<S> {
76        LateStructDescriptor::wrap(self)
77    }
78
79    /// Downcasts the descriptor to a typed [`LateStructDescriptor`] instance.
80    ///
81    /// This operation can be safely performed with [`RawLateStructDescriptor::typed`].
82    ///
83    /// ## Safety
84    ///
85    /// `S` must match the type of the [`LateStruct`] marker this descriptor is describing. Failing
86    /// to do so will not cause immediate undefined behavior but could cause latent undefined
87    /// behavior if passed to a method expecting the descriptor to belong to a certain
88    /// [`LateStruct`] type.
89    pub unsafe fn typed_unchecked<S: LateStruct>(&self) -> &LateStructDescriptor<S> {
90        unsafe { LateStructDescriptor::wrap_unchecked(self) }
91    }
92}
93
94/// A raw (untyped) descriptor for a [`LateField`].
95///
96/// You can obtain a reference to this descriptor using [`LateField::raw_descriptor`].
97///
98/// Unlike [`LateFieldDescriptor`], the type of the [`LateStruct`] to which this descriptor belongs
99/// is erased. There is no descriptor which also specifies the type of the [`LateField`] since it
100/// would be redundant.
101#[derive(Debug)]
102pub struct RawLateFieldDescriptor {
103    /// The index of this field in the struct.
104    // This needs to be accessed in `init`.
105    pub(crate) index: AtomicUsize,
106
107    /// The byte offset of this field in the struct.
108    // This needs to be accessed in `init`.
109    pub(crate) offset: AtomicUsize,
110
111    /// The layout of this field's type.
112    layout: Layout,
113
114    /// Initializes the supplied `*mut MaybeUninit<Key::Value>`.
115    init: unsafe fn(*mut u8),
116
117    /// Drops the supplied `*mut Key::Value` in place.
118    drop: unsafe fn(*mut u8),
119
120    /// Transforms the supplied `*mut Key::Value` in the first parameter into a
121    /// `*mut Struct::EraseTo` and writes it out into the `*mut *mut Struct::EraseTo` pointee in the
122    /// second parameter.
123    as_erased: fn(*mut u8, *mut ()),
124
125    /// Fetches the type name of `Key`.
126    key_type_name: fn() -> &'static str,
127
128    /// Fetches the type ID of `Key`.
129    key_type_id: fn() -> TypeId,
130
131    /// Fetches the type ID of `Struct`.
132    parent_struct: fn() -> &'static RawLateStructDescriptor,
133}
134
135impl RawLateFieldDescriptor {
136    pub(crate) const fn new<S, F>() -> Self
137    where
138        S: LateStruct,
139        F: LateField<S>,
140    {
141        Self {
142            index: AtomicUsize::new(usize::MAX),
143            offset: AtomicUsize::new(usize::MAX),
144            layout: Layout::new::<F::Value>(),
145            init: |ptr| unsafe { ptr.cast::<F::Value>().write(<F::Value>::default()) },
146            drop: |ptr| unsafe { ptr.cast::<F::Value>().drop_in_place() },
147            as_erased: |ptr, write_out| unsafe {
148                write_out
149                    .cast::<*mut S::EraseTo>()
150                    .write(F::coerce(ptr.cast::<F::Value>()));
151            },
152            key_type_name: type_name::<F>,
153            key_type_id: TypeId::of::<F>,
154            parent_struct: S::raw_descriptor,
155        }
156    }
157
158    /// Fetches the descriptor of the struct to which this field belongs.
159    pub fn parent_struct(&self) -> &'static RawLateStructDescriptor {
160        (self.parent_struct)()
161    }
162
163    /// Fetches the index of the field within the [RawLateStructDescriptor::fields] list of the
164    /// struct to which this field belongs. The ordering of these indices is not stable across
165    /// compilations.
166    pub fn index(&self, token: LateLayoutInitToken) -> usize {
167        let _ = token;
168
169        self.index.load(Relaxed)
170    }
171
172    /// Fetches the offset of this field's value within the overall struct.
173    pub fn offset(&self, token: LateLayoutInitToken) -> usize {
174        let _ = token;
175
176        self.offset.load(Relaxed)
177    }
178
179    /// Fetches the layout of the field's value.
180    pub fn layout(&self) -> Layout {
181        self.layout
182    }
183
184    /// Initializes `value` to an instance of the field's value obtained by calling its [`Default`]
185    /// implementation.
186    ///
187    /// ## Safety
188    ///
189    /// `value` must point to an uninitialized location in memory large enough to accommodate the
190    /// field's value.
191    pub unsafe fn init(&self, value: *mut u8) {
192        unsafe { (self.init)(value) }
193    }
194
195    /// Drops the instance of the field's value pointed to by `value`.
196    ///
197    /// ## Safety
198    ///
199    /// `value` must point to an initialized instance of the field's value.
200    pub unsafe fn drop(&self, value: *mut u8) {
201        unsafe { (self.drop)(value) }
202    }
203
204    /// Adorns an untyped pointer in memory with the metadata required to become a pointer to
205    /// the specified [`LateStruct`]'s [`S::EraseTo`](LateStruct::EraseTo) type.
206    ///
207    /// `value` need not point to a valid location in memory.
208    ///
209    /// This operation can be performed safely by converting this descriptor into a
210    /// [`LateFieldDescriptor`] using the [`RawLateFieldDescriptor::typed`] method and calling its
211    /// [`LateFieldDescriptor::erase_value`] method.
212    ///
213    /// ## Safety
214    ///
215    /// `S` must match the type of the [`LateStruct`] marker this descriptor is describing.
216    ///
217    pub unsafe fn erase_value<S: LateStruct>(&self, value: *mut u8) -> *mut S::EraseTo {
218        debug_assert_eq!(TypeId::of::<S>(), self.parent_struct().type_id());
219
220        let mut out = MaybeUninit::<*mut S::EraseTo>::uninit();
221
222        unsafe {
223            (self.as_erased)(value, out.as_mut_ptr().cast());
224            out.assume_init()
225        }
226    }
227
228    /// Fetches the [`type_name`] of the [`LateField`] instance—that is, the name of the marker type.
229    pub fn key_type_name(&self) -> &'static str {
230        (self.key_type_name)()
231    }
232
233    /// Fetches the [`TypeId`] of the [`LateField`] instance—that is, the ID of the marker type.
234    pub fn key_type_id(&self) -> TypeId {
235        (self.key_type_id)()
236    }
237
238    /// Downcasts the descriptor to a typed [`LateFieldDescriptor`] instance.
239    ///
240    /// `S` must match the type of the [`LateStruct`] marker to which this descriptor belongs or the
241    /// method will panic.
242    pub fn typed<S: LateStruct>(&self) -> &LateFieldDescriptor<S> {
243        LateFieldDescriptor::wrap(self)
244    }
245
246    /// Downcasts the descriptor to a typed [`LateFieldDescriptor`] instance.
247    ///
248    /// This operation can be safely performed with [`RawLateFieldDescriptor::typed`].
249    ///
250    /// ## Safety
251    ///
252    /// `S` must match the type of the [`LateStruct`] marker to which this descriptor belongs.
253    /// Failing to do so will not cause immediate undefined behavior but could cause latent
254    /// undefined behavior if passed to a method expecting the descriptor to belong to a certain
255    /// [`LateStruct`] type.
256    pub unsafe fn typed_unchecked<S: LateStruct>(&self) -> &LateFieldDescriptor<S> {
257        unsafe { LateFieldDescriptor::wrap_unchecked(self) }
258    }
259}
260
261// === Newtypes === //
262
263/// A typed descriptor for a [`LateStruct`].
264///
265/// You can obtain a reference to this descriptor using [`LateStruct::descriptor`].
266///
267/// Unlike [`RawLateStructDescriptor`], this descriptor encodes the type of the `LateStruct` it's
268/// describing.
269#[derive(Debug)]
270#[repr(transparent)]
271pub struct LateStructDescriptor<S: LateStruct> {
272    _ty: PhantomData<fn(S) -> S>,
273    raw: RawLateStructDescriptor,
274}
275
276// Conversions
277impl<S: LateStruct> LateStructDescriptor<S> {
278    fn wrap(raw: &RawLateStructDescriptor) -> &LateStructDescriptor<S> {
279        assert_eq!(raw.type_id(), TypeId::of::<S>());
280
281        unsafe { Self::wrap_unchecked(raw) }
282    }
283
284    const unsafe fn wrap_unchecked(raw: &RawLateStructDescriptor) -> &LateStructDescriptor<S> {
285        unsafe { &*(raw as *const RawLateStructDescriptor as *const LateStructDescriptor<S>) }
286    }
287
288    /// Erases the type information from this descriptor, producing its equivalent
289    /// [`RawLateStructDescriptor`].
290    pub const fn raw(&self) -> &RawLateStructDescriptor {
291        &self.raw
292    }
293}
294
295// Forwards
296impl<S: LateStruct> LateStructDescriptor<S> {
297    /// Forwards to [`RawLateStructDescriptor::layout`].
298    pub fn layout(&self, token: LateLayoutInitToken) -> Layout {
299        self.raw.layout(token)
300    }
301
302    /// Forwards to [`RawLateStructDescriptor::fields`].
303    pub fn fields(&self, token: LateLayoutInitToken) -> &'static [&'static LateFieldDescriptor<S>] {
304        unsafe { LateFieldDescriptor::wrap_slice_unchecked(self.raw.fields(token)) }
305    }
306}
307
308/// A typed descriptor for a [`LateField`].
309///
310/// You can obtain a reference to this descriptor using [`LateField::descriptor`].
311///
312/// Unlike [`LateFieldDescriptor`], this descriptor encodes the type of the `LateStruct` to which
313/// the field belongs.
314#[derive(Debug)]
315#[repr(transparent)]
316pub struct LateFieldDescriptor<S: LateStruct> {
317    _ty: PhantomData<fn(S) -> S>,
318    raw: RawLateFieldDescriptor,
319}
320
321// Conversions
322impl<S: LateStruct> LateFieldDescriptor<S> {
323    fn wrap(raw: &RawLateFieldDescriptor) -> &LateFieldDescriptor<S> {
324        assert_eq!(raw.parent_struct().type_id(), TypeId::of::<S>());
325
326        unsafe { Self::wrap_unchecked(raw) }
327    }
328
329    const unsafe fn wrap_unchecked(raw: &RawLateFieldDescriptor) -> &LateFieldDescriptor<S> {
330        unsafe { &*(raw as *const RawLateFieldDescriptor as *const LateFieldDescriptor<S>) }
331    }
332
333    const unsafe fn wrap_slice_unchecked<'a, 'b>(
334        raw: &'a [&'b RawLateFieldDescriptor],
335    ) -> &'a [&'b LateFieldDescriptor<S>] {
336        unsafe {
337            slice::from_raw_parts(raw.as_ptr().cast::<&'b LateFieldDescriptor<S>>(), raw.len())
338        }
339    }
340
341    /// Erases the type information from this descriptor, producing its equivalent
342    /// [`RawLateFieldDescriptor`].
343    pub const fn raw(&self) -> &RawLateFieldDescriptor {
344        &self.raw
345    }
346}
347
348// Forwards
349impl<S: LateStruct> LateFieldDescriptor<S> {
350    /// Forwards to [`RawLateFieldDescriptor::index`].
351    pub fn index(&self, token: LateLayoutInitToken) -> usize {
352        self.raw.index(token)
353    }
354
355    /// Forwards to [`RawLateFieldDescriptor::offset`].
356    pub fn offset(&self, token: LateLayoutInitToken) -> usize {
357        self.raw.offset(token)
358    }
359
360    /// Forwards to [`RawLateFieldDescriptor::layout`].
361    pub fn layout(&self) -> Layout {
362        self.raw.layout()
363    }
364
365    /// Forwards to [`RawLateFieldDescriptor::init`].
366    ///
367    /// ## Safety
368    ///
369    /// See safety docs of `RawLateFieldDescriptor::init`.
370    ///
371    pub unsafe fn init(&self, value: *mut u8) {
372        unsafe { self.raw.init(value) }
373    }
374
375    /// Forwards to [`RawLateFieldDescriptor::drop`].
376    ///
377    /// ## Safety
378    ///
379    /// See safety docs of `RawLateFieldDescriptor::drop`.
380    ///
381    pub unsafe fn drop(&self, value: *mut u8) {
382        unsafe { self.raw.drop(value) }
383    }
384
385    /// Forwards to [`RawLateFieldDescriptor::erase_value`] but uses type information to make the
386    /// operation safe.
387    #[allow(clippy::not_unsafe_ptr_arg_deref)] // We don't dereference `value`
388    pub fn erase_value(&self, value: *mut u8) -> *mut S::EraseTo {
389        unsafe { self.raw.erase_value::<S>(value) }
390    }
391
392    /// Forwards to [`RawLateFieldDescriptor::key_type_name`].
393    pub fn key_type_name(&self) -> &'static str {
394        self.raw.key_type_name()
395    }
396
397    /// Forwards to [`RawLateFieldDescriptor::key_type_id`].
398    pub fn key_type_id(&self) -> TypeId {
399        self.raw.key_type_id()
400    }
401
402    /// Forwards to [`RawLateFieldDescriptor::parent_struct`] but preserves the type annotation.
403    pub fn parent_struct(&self) -> &'static LateStructDescriptor<S> {
404        unsafe { self.raw.parent_struct().typed_unchecked() }
405    }
406}