dungeon_cell/vtable/
lifetime_vtable.rs

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