dungeon_cell/vtable/
bound_impl.rs

1use core::marker::PhantomData;
2use core::mem::MaybeUninit;
3use core::ptr::addr_of;
4
5use crate::bound::{Dynamic, Subset};
6use crate::marker_traits::IsBound;
7
8/// Implementation based on dynamic bound `B`.
9///
10/// This struct provides the ability to type erase the implementation of `drop`, `clone`, and
11/// `Debug::fmt`. This type should be stored by a vtable to provide the implementations for those
12/// methods.
13///
14/// # Invariants
15/// It is safe to transmute (either via the actual transmute function, or pointers)
16/// from `BoundsImpl<A>` to `BoundsImpl<B>` where `B: Subset<A>` as given by
17/// [`Subset`][crate::bound::Subset]. This allows for the implementation of
18/// [`VTableSubset`][super::VTableSubset] for borrows.
19#[repr(C)]
20#[derive(Clone, Copy)]
21pub struct BoundsImpl<B> {
22    drop_impl: unsafe fn(data: *mut MaybeUninit<u8>),
23    clone_impl: unsafe fn(
24        data: *const MaybeUninit<u8>,
25        data_other: *mut MaybeUninit<u8>,
26    ),
27    debug_impl: unsafe fn(
28        data: *const MaybeUninit<u8>,
29        f: *mut ::core::fmt::Formatter<'_>,
30    ) -> Result<(), ::core::fmt::Error>,
31    _phantom: PhantomData<B>,
32}
33
34impl<B: IsBound> BoundsImpl<B> {
35    /// Automatically extract the implementations from the type.
36    ///
37    /// - [`Self::drop_value()`] is always implemented using [`drop_in_place`][std::ptr::drop_in_place].
38    /// - [`Self::clone_value()`] will only be available if `B` contains the `Clone` bound.
39    /// - [`Self::debug_value()`] will only be available if `B` contains the `Debug` bound.
40    pub const fn new<T: Dynamic<B>>() -> Self {
41        Self {
42            drop_impl: drop_t_in_place::<T>,
43            clone_impl: clone_t::<T, B>,
44            debug_impl: debug_t::<T, B>,
45            _phantom: PhantomData,
46        }
47    }
48
49    /// Convert into a [`BoundsImpl`] with less trait bounds.
50    ///
51    /// The implementations will remain the same. This is safe to do because only
52    /// adding traits to a bound could cause a implementation to be ran when it's not
53    /// implemented already. Which can't happen with the `Bnew: Subset<B>` bound.
54    pub const fn into_subset<Bnew: IsBound>(self) -> BoundsImpl<Bnew>
55    where
56        Bnew: Subset<B>,
57    {
58        BoundsImpl {
59            drop_impl: self.drop_impl,
60            clone_impl: self.clone_impl,
61            debug_impl: self.debug_impl,
62            _phantom: PhantomData,
63        }
64    }
65
66    /// Drop value.
67    ///
68    /// The value will be invalid after calling this on it.
69    ///
70    /// # Safety
71    /// The `data` buffer must contain a valid value of the type
72    /// this [`BoundsImpl`] was created for.
73    pub unsafe fn drop_value(&self, data: *mut MaybeUninit<u8>) {
74        // SAFETY: Called via drop_value.
75        unsafe {
76            (self.drop_impl)(data);
77        }
78    }
79
80    /// Clone value.
81    ///
82    /// # Safety
83    /// - `B::BOUND_BY_CLONE` must be `true` and/or `B::CloneMarker: Clone`.
84    /// - The `data` buffer must contain a valid value of the type
85    /// this [`BoundsImpl`] was created for.
86    /// - The `data_other` buffer must be large enough and aligned properly
87    /// to store the value.
88    pub unsafe fn clone_value(
89        &self,
90        data: *const MaybeUninit<u8>,
91        data_other: *mut MaybeUninit<u8>,
92    ) {
93        // SAFETY: Called via clone_value.
94        unsafe {
95            (self.clone_impl)(data, data_other);
96        }
97    }
98
99    /// Debug format value.
100    ///
101    /// # Safety
102    /// - `B::BOUND_BY_DEBUG` must be `true` and/or `B::DebugMarker: Debug`.
103    /// - The `data` buffer must contain a valid value of the type
104    /// this [`BoundsImpl`] was created for.
105    pub unsafe fn debug_value(
106        &self,
107        data: *const MaybeUninit<u8>,
108        f: &mut ::core::fmt::Formatter<'_>,
109    ) -> Result<(), ::core::fmt::Error> {
110        // SAFETY: Called via debug_value.
111        unsafe { (self.debug_impl)(data, f) }
112    }
113}
114
115/// Drop a `T` in a buffer.
116///
117/// # Saftey
118/// Must only be called through [`BoundsImpl::drop_value`].
119unsafe fn drop_t_in_place<T>(data: *mut MaybeUninit<u8>) {
120    // SAFETY:
121    // We are allowed make `data` into a valid `T` because
122    // of the safety requirements of BoundsImpl::drop_value.
123    unsafe { ::core::ptr::drop_in_place::<T>(data.cast::<T>()) }
124}
125
126/// Clone a `T` in a buffer.
127///
128/// # Saftey
129/// Must only be called through [`BoundsImpl::clone_value`].
130unsafe fn clone_t<T, B: IsBound>(
131    data: *const MaybeUninit<u8>,
132    other_data: *mut MaybeUninit<u8>,
133) where
134    T: Dynamic<B>,
135{
136    // SAFETY: We know that data has a value of T via clone_value's safety requirements.
137    let value = unsafe { &*data.cast::<T>() };
138
139    // SAFETY: We know that B has Clone because of clone_value's safety requirements.
140    let cloned = MaybeUninit::new(unsafe { T::clone_unchecked(value) });
141
142    // SAFETY: We know cloned is valid to read (and initialized) because we just made it.
143    // We also know other_data can have a value of T written into it because of
144    // clone_value's safety requirements.
145    // And we know they aren't overlaping because we just made cloned.
146    unsafe {
147        ::core::ptr::copy_nonoverlapping(
148            addr_of!(cloned),
149            other_data.cast::<MaybeUninit<T>>(),
150            1,
151        )
152    }
153
154    // The drop code for cloned will not run here because its in a MaybeUninit.
155}
156
157/// Debug format a `T` in a buffer.
158///
159/// # Saftey
160/// Must only be called through [`BoundsImpl::debug_value`].
161unsafe fn debug_t<T, B: IsBound>(
162    data: *const MaybeUninit<u8>,
163    f: *mut ::core::fmt::Formatter<'_>,
164) -> Result<(), ::core::fmt::Error>
165where
166    T: Dynamic<B>,
167{
168    // SAFETY: By the safety requirements of debug_value we know data has a valid T.
169    let value = unsafe { &*data.cast::<T>() };
170
171    // SAFETY: Because this is called by debug_value we know that it was just a borrow.
172    // We had to convert it to a pointer to allow const constructing a BoundsImpl.
173    let f = unsafe { &mut *f };
174
175    // SAFETY: We know B has Debug because of the safety requirements of debug_value.
176    unsafe { T::debug_unchecked(value, f) }
177}