dungeon_cell/vtable.rs
1//! Virtual method tables.
2//!
3//! [`dungeon_cell`][crate] uses custom vtables to track what types are stored and
4//! provide methods for those stored types.
5
6use ::core::cmp::Ordering;
7use ::core::fmt;
8
9// mod coerce;
10mod bound_impl;
11mod lifetime_vtable;
12mod static_vtable;
13
14use crate::bound::Dynamic;
15use crate::layout::LayoutType;
16use crate::marker_traits::{IsBound, IsLayout};
17
18pub use self::lifetime_vtable::*;
19pub use self::static_vtable::*;
20pub use bound_impl::BoundsImpl;
21// pub use coerce::*;
22
23/// Inspect type to get a `T`.
24///
25/// This is used to get things like type IDs.
26///
27/// # Safety
28/// The returned `T` for a `Self` must always be the same.
29pub unsafe trait Inspect<T> {
30 /// Get the `T` for `Self`.
31 fn inspect() -> T;
32}
33
34/// Virtual method table and type descriptor.
35///
36/// Rust automatically generates vtables for trait objects. However, its not currently
37/// possible to access these vtables directly on stable. Instead, this trait provides
38/// a unified interface for custom vtables. Custom vtables can be generated thanks to
39/// Rust's ability to static promote constants to give them stable addresses.
40///
41/// A vtable is created from a type [`Descriptor`] and a [`BoundsImpl`]. A `Descriptor`
42/// describes a type, and includes things like the type's size and name. The
43/// [`Descriptor::is_type()`] method can be used to check if a vtable is for a particular type. A
44/// `BoundsImpl` contains the implementations of the virtual methods for the type.
45/// Currently, only [`Drop`][std::ops::Drop], [`Clone`][std::clone::Clone], and
46/// [`Debug`][std::fmt::Debug] are included in the virtual methods.
47///
48/// All [`dungeon_cell`][crate] types use this trait as the basis for their type erasure.
49///
50/// Use [`VTableOf`] or [`ConstVTableOf`] to construct a instance of a generic vtable for a type.
51/// Additionally, use [`VTableSubset`] to reduce the number of traits bound by [`Self::Bounds`].
52///
53/// # Safety
54/// - The type described by [`Self::descriptor()`] must be the same that [`Self::bound_impl`] was
55/// created for. This means calling [`Descriptor::new::<T>()`][Descriptor::new] and
56/// [`BoundsImpl::new::<T>()`][BoundsImpl::new] with the same `T`.
57/// - The type [`Self::Id`] must be well behaved. This means that when performing equality
58/// between two [`Self::Id`] if the result is `true` then the type `T` that [`Self::descriptor()`]
59/// describes is the same type. Additionally, [`Inspect::<Self::Id>::inspect::<T>()`][Inspect::inspect] must return the
60/// [`Self::Id`] for the type `T` following this equality.
61/// - The type the vtable describes must implement the traits given by [`Self::Bounds`].
62pub unsafe trait VTable: Clone {
63 /// The dynamic trait bounds applied.
64 ///
65 /// This may not match the trait bounds used when creating the vtable,
66 /// but it will always be equal to or a subset of it.
67 type Bounds: IsBound;
68
69 /// Type to store the ID information for which type the vtable is for.
70 ///
71 /// ID types must always implement `Eq` to allow comparing them.
72 type Id: PartialEq + Eq;
73
74 /// Get the type descriptor.
75 ///
76 /// This is the descriptor for the type the vtable was made for.
77 fn descriptor(&self) -> &Descriptor<Self::Id>;
78
79 /// Get the bound implementation.
80 ///
81 /// This is the method implementation for the type the vtable was made for.
82 fn bound_impl(&self) -> &BoundsImpl<Self::Bounds>;
83}
84
85/// Convert vtable into a vtable with less dynamic trait bounds.
86///
87/// # Safety
88/// The type the vtable describes must not change.
89pub unsafe trait VTableSubset<B>: VTable {
90 /// The type of the new vtable.
91 type Subset: VTable<Bounds = B>;
92
93 /// Convert a vtable.
94 fn into_subset(self) -> Self::Subset;
95}
96
97/// Describes a type.
98///
99/// The static properties of a type are exposed here.
100///
101/// Equality between [`Descriptor`] instances is based only on the `Id` type.
102pub struct Descriptor<Id> {
103 get_id: fn() -> Id,
104 get_type_name: fn() -> &'static str,
105 size: usize,
106 alignment: usize,
107}
108
109impl<Id> Copy for Descriptor<Id> {}
110impl<Id> Clone for Descriptor<Id> {
111 fn clone(&self) -> Self {
112 *self
113 }
114}
115
116impl<Id: PartialEq> Descriptor<Id> {
117 /// Create a [`Descriptor`] for type `T`.
118 ///
119 /// This function can be used in const contexts.
120 pub const fn new<T: Inspect<Id>>() -> Self {
121 // The id and type_name are done in a closure because they aren't available
122 // in a const context.
123 Self {
124 get_id: || T::inspect(),
125 get_type_name: || ::core::any::type_name::<T>(),
126 size: ::core::mem::size_of::<T>(),
127 alignment: ::core::mem::align_of::<T>(),
128 }
129 }
130
131 /// Check if the [`Descriptor`] is for the type `T`.
132 ///
133 /// If this method returns `true` then the descriptor was created for type `T`.
134 pub fn is_type<T: Inspect<Id>>(&self) -> bool {
135 self.id() == T::inspect()
136 }
137
138 /// Get the ID for the type.
139 pub fn id(&self) -> Id {
140 (self.get_id)()
141 }
142
143 /// Get the name of the type.
144 ///
145 /// This uses [`std::any::type_name()`] so should only be used for diagnostics.
146 pub fn type_name(&self) -> &'static str {
147 (self.get_type_name)()
148 }
149
150 /// Size of the type in bytes.
151 pub const fn size(&self) -> usize {
152 self.size
153 }
154
155 /// Alignment of the type in bytes.
156 pub const fn alignment(&self) -> usize {
157 self.alignment
158 }
159
160 /// Alignment of the type in bytes.
161 pub const fn layout_can_store<L: IsLayout>(&self) -> bool {
162 self.size <= LayoutType::<L>::size()
163 && self.alignment <= LayoutType::<L>::alignment()
164 }
165}
166
167impl<Id: PartialEq> fmt::Debug for Descriptor<Id> {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 f.debug_struct("Descriptor")
170 .field("type_name", &self.type_name())
171 .field("size", &self.size())
172 .field("alignment", &self.alignment())
173 .finish()
174 }
175}
176
177impl<Id: PartialEq> PartialEq for Descriptor<Id> {
178 fn eq(&self, other: &Self) -> bool {
179 self.id() == other.id()
180 }
181}
182
183impl<Id: PartialEq> Eq for Descriptor<Id> {}
184
185impl<Id: PartialEq + PartialOrd> PartialOrd for Descriptor<Id> {
186 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
187 self.id().partial_cmp(&other.id())
188 }
189}
190
191impl<Id: PartialEq + Ord> Ord for Descriptor<Id> {
192 fn cmp(&self, other: &Self) -> Ordering {
193 self.id().cmp(&other.id())
194 }
195}
196
197impl<Id: PartialEq + ::core::hash::Hash> ::core::hash::Hash for Descriptor<Id> {
198 fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
199 self.id().hash(state);
200 }
201}
202
203// Automatically implement for borrows of a vtable.
204// This allows for borrows to static vtable instances to be valid.
205//
206// SAFETY: Forwards everything to the impl of `T`.
207unsafe impl<T: VTable> VTable for &T {
208 type Bounds = T::Bounds;
209
210 type Id = T::Id;
211
212 fn descriptor(&self) -> &Descriptor<Self::Id> {
213 T::descriptor(self)
214 }
215
216 fn bound_impl(&self) -> &BoundsImpl<Self::Bounds> {
217 T::bound_impl(self)
218 }
219}
220
221/// Generate [`VTable`] instance for type.
222///
223/// The type `T` must satisfy the dynamic trait bounds given by `Self::Bounds`.
224///
225/// # Safety
226/// - The vtable instance [`Self::instance()`] returns must be for the type `T`.
227/// This means [`VTable::descriptor().is_type::<T>()`][Descriptor::is_type] must return `true`.
228/// - The [`Self::instance()`] method must not panic.
229pub unsafe trait VTableOf<T: Dynamic<Self::Bounds>>: VTable {
230 /// Get an instance of the vtable for the type `T`.
231 fn instance() -> Self;
232}
233
234/// Generate [`VTable`] instance for type in const context.
235///
236/// The type `T` must satisfy the dynamic trait bounds given by `Self::Bounds`.
237///
238/// This is the const form of [`VTableOf`]. Because traits can't have const methods
239/// the instance is an associated constant.
240///
241/// # Safety
242/// - The vtable instance [`Self::INSTANCE`] must be for the type `T`.
243/// This means [`VTable::descriptor().is_type::<T>()`][Descriptor::is_type] must return `true`.
244pub unsafe trait ConstVTableOf<T: Dynamic<Self::Bounds>>:
245 VTableOf<T>
246{
247 /// Instance of the vtable for the type `T`.
248 const INSTANCE: Self;
249}