dyn_dyn/
table.rs

1use crate::cast_target::DynDynCastTarget;
2use cfg_if::cfg_if;
3use core::any::TypeId;
4use core::fmt::{self, Debug};
5use core::marker::Unsize;
6use core::mem;
7use core::ptr::{self, DynMetadata, Pointee};
8
9#[cfg(doc)]
10use crate::dyn_dyn_cast;
11
12/// An untyped metadata that corresponds to the metadata that would be used for a trait object.
13#[derive(Debug, Clone, Copy)]
14pub struct AnyDynMetadata(*const ());
15
16impl AnyDynMetadata {
17    /// Upcasts this typed metadata into untyped metadata.
18    pub const fn upcast<T: ?Sized>(meta: DynMetadata<T>) -> AnyDynMetadata {
19        // SAFETY: There are no invalid values for *const (), so transmuting to it should never cause UB if DynMetadata<T> is of the same
20        //         size. The only valid usage of this *const () is then to transmute it back to DynMetadata<T>. While this definitely makes
21        //         assumptions about how the standard library implements DynMetadata (which is unfortunate), we should fail to compile if
22        //         that changes rather than invoking UB.
23        unsafe { AnyDynMetadata(mem::transmute::<DynMetadata<T>, *const ()>(meta)) }
24    }
25
26    /// Downcasts this untyped metadata into typed metadata for a trait object referring to a particular trait.
27    ///
28    /// # Safety
29    ///
30    /// This untyped metadata must have originally been constructed by converting a `DynMetadata<T>`.
31    pub const unsafe fn downcast<T: DynDynCastTarget + ?Sized>(self) -> DynMetadata<T> {
32        // SAFETY: Safety invariants for this fn require that this pointer came from transmuting a DynMetadata<T>, so transmuting it back
33        //         should be safe.
34        unsafe { mem::transmute::<*const (), DynMetadata<T>>(self.0) }
35    }
36}
37
38impl<T: ?Sized> From<DynMetadata<T>> for AnyDynMetadata {
39    fn from(meta: DynMetadata<T>) -> Self {
40        AnyDynMetadata::upcast(meta)
41    }
42}
43
44// SAFETY: Since DynMetadata<T>: Send for all T, AnyDynMetadata should also be Send
45unsafe impl Send for AnyDynMetadata {}
46
47// SAFETY: Since DynMetadata<T>: Sync for all T, AnyDynMetadata should also be Sync
48unsafe impl Sync for AnyDynMetadata {}
49
50cfg_if! {
51    if #[cfg(feature = "dynamic-names")] {
52        type TypeName = &'static str;
53
54        const fn type_name<T: ?Sized>() -> TypeName {
55            core::any::type_name::<T>()
56        }
57    } else {
58        #[derive(Debug, Clone, Copy)]
59        struct TypeName;
60
61        #[allow(clippy::extra_unused_type_parameters)]
62        const fn type_name<T: ?Sized>() -> TypeName { TypeName }
63    }
64}
65
66#[derive(Debug, Clone, Copy)]
67struct DynInfo(TypeId, TypeName);
68
69impl DynInfo {
70    pub const fn of<T: 'static + ?Sized>() -> DynInfo {
71        DynInfo(TypeId::of::<T>(), type_name::<T>())
72    }
73
74    pub fn type_id(self) -> TypeId {
75        self.0
76    }
77
78    #[cfg(feature = "dynamic-names")]
79    pub fn name(self) -> &'static str {
80        self.1
81    }
82}
83
84impl PartialEq for DynInfo {
85    fn eq(&self, other: &Self) -> bool {
86        self.0 == other.0
87    }
88}
89
90impl Eq for DynInfo {}
91
92/// An entry in a concrete type's table of downcast-exposed traits.
93///
94/// Each entry represents a single trait object that the concrete type in question can be downcast to. Note that entries will only appear
95/// for bare trait object types, i.e. `dyn Trait`. Trait objects with extra marker types, e.g. `dyn Trait + Send`, are handled specially
96/// by the [`dyn_dyn_cast!`] macro and do not appear in a concrete type's trait table.
97pub struct DynDynTableEntry {
98    ty: DynInfo,
99    meta: AnyDynMetadata,
100}
101
102impl DynDynTableEntry {
103    const fn meta_for_ty<
104        T: Unsize<D>,
105        D: ?Sized + Pointee<Metadata = DynMetadata<M>>,
106        M: ?Sized,
107    >() -> DynMetadata<M> {
108        ptr::metadata(ptr::null::<T>() as *const D)
109    }
110
111    #[doc(hidden)]
112    pub const fn new<
113        T: Unsize<D>,
114        D: ?Sized + Pointee<Metadata = DynMetadata<M>> + 'static,
115        M: ?Sized,
116    >() -> DynDynTableEntry {
117        DynDynTableEntry {
118            ty: DynInfo::of::<D>(),
119            meta: AnyDynMetadata::upcast(Self::meta_for_ty::<T, D, M>()),
120        }
121    }
122
123    /// Gets the [`TypeId`] of the trait object corresponding to this entry.
124    pub fn type_id(&self) -> TypeId {
125        self.ty.type_id()
126    }
127
128    /// Gets a human-readable name representing the trait object corresponding to this entry.
129    #[cfg(feature = "dynamic-names")]
130    pub fn type_name(&self) -> &'static str {
131        self.ty.name()
132    }
133}
134
135impl Debug for DynDynTableEntry {
136    #[cfg(feature = "dynamic-names")]
137    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138        write!(
139            f,
140            "DynDynTableEntry(<{}>: {:?})",
141            self.type_name(),
142            self.meta
143        )
144    }
145
146    #[cfg(not(feature = "dynamic-names"))]
147    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148        write!(f, "DynDynTableEntry({:?}: {:?})", self.type_id(), self.meta)
149    }
150}
151
152/// A table of trait object types that a concrete type can be downcast to.
153#[derive(Debug, Clone, Copy)]
154pub struct DynDynTable {
155    traits: &'static [DynDynTableEntry],
156}
157
158impl DynDynTable {
159    /// Finds the metadata corresponding to the type with the provided [`TypeId`] in this table or `None` if no such metadata is present.
160    pub fn find_untyped(&self, type_id: TypeId) -> Option<AnyDynMetadata> {
161        self.traits
162            .iter()
163            .find(|&entry| entry.ty.type_id() == type_id)
164            .map(|entry| entry.meta)
165    }
166
167    /// Finds the metadata corresponding to the trait `D` in this table or `None` if no such metadata is present.
168    pub fn find<D: ?Sized + DynDynCastTarget + 'static>(&self) -> Option<DynMetadata<D>> {
169        self.find_untyped(TypeId::of::<D>()).map(|meta| {
170            // SAFETY: This metadata corresponds to the trait D, so we can downcast it
171            unsafe { meta.downcast() }
172        })
173    }
174
175    /// Returns a reference to the slice of entries in this table
176    pub fn into_slice(self) -> &'static [DynDynTableEntry] {
177        self.traits
178    }
179
180    #[doc(hidden)]
181    pub const fn new(traits: &'static [DynDynTableEntry]) -> DynDynTable {
182        DynDynTable { traits }
183    }
184}
185
186impl IntoIterator for DynDynTable {
187    type Item = &'static DynDynTableEntry;
188    type IntoIter = DynDynTableIterator;
189
190    fn into_iter(self) -> Self::IntoIter {
191        DynDynTableIterator(self.traits.iter())
192    }
193}
194
195/// An iterator returning all entries in a [`DynDynTable`].
196pub struct DynDynTableIterator(core::slice::Iter<'static, DynDynTableEntry>);
197
198impl Iterator for DynDynTableIterator {
199    type Item = &'static DynDynTableEntry;
200
201    fn next(&mut self) -> Option<Self::Item> {
202        self.0.next()
203    }
204}