dungeon_cell/vtable/
static_vtable.rs

1use core::any::TypeId;
2use core::fmt;
3
4use crate::bound::{Dynamic, Subset};
5use crate::marker_traits::IsBound;
6
7use super::{
8    BoundsImpl, ConstVTableOf, Descriptor, Inspect, VTable, VTableOf,
9    VTableSubset,
10};
11
12/// Implementation of [`VTable`] for `'static` types.
13///
14/// Static instances of [`StaticVTable`] can be made automatically for any type
15/// using the [`VTableOf`] or [`ConstVTableOf`] trait. These instances are created using automatic static
16/// promotion and therefore have no runtime overhead to create them.
17///
18/// # Examples
19///
20/// ```
21/// use dungeon_cell::vtable::{StaticVTable, VTableOf, VTable};
22/// use dungeon_cell::bound::bounds;
23///
24/// // get vtable for i32
25/// let vtable = StaticVTable::<bounds::Normal>::new::<i32>();
26///
27/// assert_eq!(vtable.descriptor().type_name(), "i32");
28/// assert_eq!(vtable.descriptor().size(), 4);
29/// assert_eq!(vtable.descriptor().alignment(), 4);
30///
31/// // get static vtable for f64
32/// let vtable = <&'static StaticVTable<bounds::Normal> as VTableOf<f64>>::instance();
33///
34/// assert_eq!(vtable.descriptor().type_name(), "f64");
35/// assert_eq!(vtable.descriptor().size(), 8);
36/// assert_eq!(vtable.descriptor().alignment(), 8);
37/// ```
38#[derive(Clone)]
39#[repr(C)]
40pub struct StaticVTable<B> {
41    bound_impl: BoundsImpl<B>,
42    descriptor: Descriptor<Static>,
43}
44
45impl<B> fmt::Debug for StaticVTable<B> {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        f.debug_struct("StaticVTable")
48            .field("type_name", &self.descriptor.type_name())
49            .field("type_id", &self.descriptor.id().0)
50            .field("size", &self.descriptor.size)
51            .field("alignment", &self.descriptor.alignment)
52            .finish()
53    }
54}
55
56impl<B: IsBound> StaticVTable<B> {
57    /// Return the const instance of the vtable for `T`.
58    pub const fn new<T: 'static>() -> Self
59    where
60        T: Dynamic<B>,
61    {
62        <Self as ConstVTableOf<T>>::INSTANCE
63    }
64}
65
66/// Static type ID.
67///
68/// Available for all `'static` types.
69///
70/// # Examples
71/// ```
72/// use dungeon_cell::vtable::{Static, Inspect};
73///
74/// assert_ne!(
75///     <i32 as Inspect<Static>>::inspect(),
76///     <f32 as Inspect<Static>>::inspect()
77/// );
78/// ```
79#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
80pub struct Static(TypeId);
81
82// SAFETY: TypeId is well behaved and we always return the one for T.
83unsafe impl<T: 'static> Inspect<Static> for T {
84    fn inspect() -> Static {
85        Static(TypeId::of::<T>())
86    }
87}
88
89// SAFETY:
90// The descriptor and bounds impl are always in sync.
91// The Static Id type is well behaved because its built on TypeId.
92unsafe impl<B: IsBound> VTable for StaticVTable<B> {
93    type Bounds = B;
94
95    type Id = Static;
96
97    fn descriptor(&self) -> &super::Descriptor<Self::Id> {
98        &self.descriptor
99    }
100
101    fn bound_impl(&self) -> &BoundsImpl<Self::Bounds> {
102        &self.bound_impl
103    }
104}
105
106// SAFETY: The type we describe doesnt change.
107unsafe impl<B: IsBound, Bnew: IsBound> VTableSubset<Bnew> for StaticVTable<B>
108where
109    Bnew: Subset<B>,
110{
111    type Subset = StaticVTable<Bnew>;
112
113    fn into_subset(self) -> Self::Subset {
114        StaticVTable {
115            bound_impl: self.bound_impl.into_subset::<Bnew>(),
116            descriptor: self.descriptor,
117        }
118    }
119}
120
121// SAFETY: The type we describe doesnt change.
122unsafe impl<'a, B: IsBound, Bnew: IsBound> VTableSubset<Bnew>
123    for &'a StaticVTable<B>
124where
125    Bnew: Subset<B>,
126{
127    type Subset = &'a StaticVTable<Bnew>;
128
129    fn into_subset(self) -> Self::Subset {
130        // SAFETY: StaticVTable is repr(C) and BoundsImpl says its safe to transmute it
131        // if we follow the Subset rule which we do.
132        // Descriptor doesnt need to be repr(C) because it has the same type.
133        unsafe {
134            &*(self as *const StaticVTable<B> as *const StaticVTable<Bnew>)
135        }
136    }
137}
138
139// SAFETY: The returned vtable is for type T and the function doesn't ever panic.
140unsafe impl<T: 'static, B: IsBound> VTableOf<T> for StaticVTable<B>
141where
142    T: Dynamic<B>,
143{
144    fn instance() -> Self {
145        Self::new::<T>()
146    }
147}
148
149// SAFETY: The returned vtable is for type T.
150unsafe impl<T: 'static, B: IsBound> ConstVTableOf<T> for StaticVTable<B>
151where
152    T: Dynamic<B>,
153{
154    const INSTANCE: Self = Self {
155        descriptor: Descriptor::new::<T>(),
156        bound_impl: BoundsImpl::new::<T>(),
157    };
158}
159
160// SAFETY: The returned vtable is for type T and the function doesn't ever panic.
161unsafe impl<'a, T: 'static, B: IsBound> VTableOf<T> for &'a StaticVTable<B>
162where
163    T: Dynamic<B>,
164{
165    fn instance() -> Self {
166        &ConstVTableOf::<T>::INSTANCE
167    }
168}
169
170// SAFETY: The returned vtable is for type T.
171unsafe impl<'a, T: 'static, B: IsBound> ConstVTableOf<T> for &'a StaticVTable<B>
172where
173    T: Dynamic<B>,
174{
175    const INSTANCE: Self = &ConstVTableOf::<T>::INSTANCE;
176}
177
178#[cfg(test)]
179mod test {
180    use core::mem::MaybeUninit;
181    use core::sync::atomic::AtomicBool;
182
183    use crate::bound::bounds;
184
185    use super::*;
186
187    #[test]
188    fn has_debug() {
189        let _ = format!("{:?}", StaticVTable::<bounds::Normal>::new::<i32>());
190    }
191
192    #[test]
193    fn can_drop_a_t() {
194        static Y: AtomicBool = AtomicBool::new(false);
195
196        struct X {
197            a: i32,
198        }
199
200        impl Drop for X {
201            fn drop(&mut self) {
202                assert_eq!(self.a, 123);
203
204                Y.store(true, core::sync::atomic::Ordering::Relaxed);
205            }
206        }
207
208        let v = StaticVTable::<bounds::Empty>::new::<X>();
209
210        let mut x = MaybeUninit::new(X { a: 123 });
211
212        unsafe { v.bound_impl().drop_value(x.as_mut_ptr() as _) };
213
214        assert!(Y.load(core::sync::atomic::Ordering::Relaxed));
215    }
216
217    #[test]
218    fn vtable_interface() {
219        fn assert_for<T: 'static>() {
220            let v = StaticVTable::<bounds::Empty>::new::<T>();
221
222            assert_eq!(v.descriptor().size(), core::mem::size_of::<T>());
223            assert_eq!(v.descriptor().alignment(), core::mem::align_of::<T>());
224            // assert_eq!(v.is_type::<T>());
225            let _ = v.descriptor().type_name();
226
227            let _ = <StaticVTable<bounds::Empty> as VTableOf<T>>::instance();
228            let _ = <&StaticVTable<bounds::Empty> as VTableOf<T>>::instance();
229
230            let _ = <StaticVTable<bounds::Empty> as ConstVTableOf<T>>::INSTANCE;
231            let _ =
232                <&StaticVTable<bounds::Empty> as ConstVTableOf<T>>::INSTANCE;
233        }
234
235        assert_for::<i32>();
236        assert_for::<String>();
237        assert_for::<()>();
238        assert_for::<u8>();
239        assert_for::<[i32; 100]>();
240    }
241}