stabby_abi/vtable/
mod.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   Pierre Avital, <pierre.avital@me.com>
13//
14
15use crate::{self as stabby, fatptr::AnonymRefMut};
16use core::hash::Hash;
17
18#[rustversion::nightly]
19/// Implementation detail for stabby's version of dyn traits.
20/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
21pub trait IConstConstructor<'a, Source>: 'a + Copy + core::marker::Freeze {
22    /// The vtable.
23    const VTABLE: Self;
24    /// A reference to the vtable
25    const VTABLE_REF: &'a Self = &Self::VTABLE;
26    /// Returns the reference to the vtable
27    fn vtable() -> &'a Self {
28        Self::VTABLE_REF
29    }
30}
31
32#[rustversion::before(1.78.0)]
33/// Implementation detail for stabby's version of dyn traits.
34/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
35pub trait IConstConstructor<'a, Source>: 'a + Copy {
36    /// A reference to the vtable
37    const VTABLE_REF: &'a Self;
38    /// Returns the reference to the vtable
39    fn vtable() -> &'a Self {
40        Self::VTABLE_REF
41    }
42}
43
44#[cfg(all(not(stabby_default_alloc = "disabled"), feature = "test"))]
45pub use internal::{VTableRegistry, VtBtree, VtVec};
46
47#[cfg(not(stabby_default_alloc = "disabled"))]
48pub(crate) mod internal {
49    use crate::alloc::{boxed::BoxedSlice, collections::arc_btree::AtomicArcBTreeSet};
50    use core::ptr::NonNull;
51    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
52    pub struct VTable(&'static [*const ()]);
53    impl PartialOrd<&[*const ()]> for VTable {
54        fn partial_cmp(&self, other: &&[*const ()]) -> Option<core::cmp::Ordering> {
55            Some(self.0.cmp(*other))
56        }
57    }
58    impl PartialEq<&[*const ()]> for VTable {
59        fn eq(&self, other: &&[*const ()]) -> bool {
60            self.0.eq(*other)
61        }
62    }
63    // SAFETY: VTables are always `Send + Sync`
64    unsafe impl Send for VTable {}
65    // SAFETY: VTables are always `Send + Sync`
66    unsafe impl Sync for VTable {}
67    use crate::alloc::{vec::Vec, DefaultAllocator};
68    /// A BTree used to store VTables.
69    ///
70    /// This is an internal API, only publically exposed for benchmarking purposes. It may change arbitrarily without warning.
71    pub type VtBtree<const SIZE: usize> = AtomicArcBTreeSet<VTable, false, SIZE>;
72    /// A Vec used to store VTables
73    ///
74    /// This is an internal API, only publically exposed for benchmarking purposes. It may change arbitrarily without warning.
75    pub type VtVec =
76        crate::alloc::sync::AtomicArc<crate::alloc::vec::Vec<VTable>, DefaultAllocator>;
77    /// A registry where VTables can be inserted via interior mutability.
78    ///
79    /// This is an internal API, only publically exposed for benchmarking purposes. It may change arbitrarily without warning.
80    pub trait VTableRegistry {
81        /// Inserts a raw vtable in the registry.
82        fn insert(&self, vtable: &[*const ()]) -> NonNull<*const ()>;
83        /// Inserts a vtable in the registry.
84        fn insert_typed<'a, Vt: Copy>(&self, vtable: &Vt) -> &'a Vt {
85            unsafe {
86                let vtable = core::slice::from_raw_parts(
87                    (vtable as *const Vt).cast(),
88                    core::mem::size_of::<Vt>() / core::mem::size_of::<*const ()>(),
89                );
90                let vt = self.insert(vtable).cast().as_ref();
91                debug_assert_eq!(
92                    core::slice::from_raw_parts(
93                        (vt as *const Vt).cast::<*const ()>(),
94                        core::mem::size_of::<Vt>() / core::mem::size_of::<*const ()>(),
95                    ),
96                    vtable
97                );
98                vt
99            }
100        }
101    }
102    impl VTableRegistry for VtVec {
103        fn insert(&self, vtable: &[*const ()]) -> NonNull<*const ()> {
104            let mut search_start = 0;
105            let mut allocated_slice: Option<BoxedSlice<_, DefaultAllocator>> = None;
106            let mut vtables = self.load(core::sync::atomic::Ordering::SeqCst);
107            loop {
108                let vts = match vtables.as_ref() {
109                    None => [].as_slice(),
110                    Some(vts) => match vts[search_start..].iter().find(|e| **e == vtable) {
111                        Some(vt) => {
112                            // SAFETY: since `vt.0` is a reference, it is guaranteed to be non-null.
113                            return unsafe { NonNull::new_unchecked(vt.0.as_ptr().cast_mut()) };
114                        }
115                        None => vts,
116                    },
117                };
118                let avt = allocated_slice.unwrap_or_else(|| BoxedSlice::from(vtable));
119                // SAFETY::`avt` will be leaked if inserting `vt` succeeds.
120                let vt = unsafe { core::mem::transmute::<&[_], &'static [_]>(avt.as_slice()) };
121                allocated_slice = Some(avt);
122                if let Err(updated) =
123                    self.is(vtables.as_ref(), core::sync::atomic::Ordering::SeqCst)
124                {
125                    vtables = updated;
126                    continue;
127                }
128                let mut vec = Vec::with_capacity(vts.len() + 1);
129                vec.copy_extend(vts);
130                vec.push(VTable(vt));
131                if let Err(updated) =
132                    self.is(vtables.as_ref(), core::sync::atomic::Ordering::SeqCst)
133                {
134                    vtables = updated;
135                    continue;
136                }
137                let vec = Some(crate::alloc::sync::Arc::new(vec));
138                match self.compare_exchange(
139                    vtables.as_ref(),
140                    vec,
141                    core::sync::atomic::Ordering::SeqCst,
142                    core::sync::atomic::Ordering::SeqCst,
143                ) {
144                    Ok(_) => {
145                        core::mem::forget(allocated_slice);
146                        return unsafe { NonNull::new_unchecked(vt.as_ptr().cast_mut()) };
147                    }
148                    Err(new_vtables) => {
149                        search_start = vts.len();
150                        vtables = new_vtables;
151                    }
152                }
153            }
154        }
155    }
156    #[allow(unknown_lints)]
157    #[allow(clippy::missing_transmute_annotations)]
158    impl<const SIZE: usize> VTableRegistry for VtBtree<SIZE> {
159        fn insert(&self, vtable: &[*const ()]) -> NonNull<*const ()> {
160            let mut ret = None;
161            let mut allocated_slice: Option<BoxedSlice<_, DefaultAllocator>> = None;
162            self.edit(|tree| {
163                let mut tree = tree.clone();
164                if let Some(vt) = tree.get(&vtable) {
165                    ret = unsafe { Some(NonNull::new_unchecked(vt.0.as_ptr().cast_mut())) };
166                    return tree;
167                }
168                let vt = match &allocated_slice {
169                    Some(vt) => unsafe { core::mem::transmute(vt.as_slice()) },
170                    None => {
171                        let vt = BoxedSlice::from(vtable);
172                        let slice = unsafe { core::mem::transmute(vt.as_slice()) };
173                        allocated_slice = Some(vt);
174                        slice
175                    }
176                };
177                tree.insert(vt);
178                tree
179            });
180            if ret.is_none() {
181                let ret = &mut ret;
182                self.get(&vtable, move |vt| {
183                    let start = unsafe {
184                        NonNull::new_unchecked(
185                            vt.expect("VTable should've been inserted by now")
186                                .0
187                                .as_ptr()
188                                .cast_mut(),
189                        )
190                    };
191                    if let Some(allocated) = allocated_slice {
192                        if allocated.slice.start.ptr == start {
193                            core::mem::forget(allocated);
194                        }
195                    }
196                    *ret = Some(start);
197                });
198            }
199            unsafe { ret.unwrap_unchecked() }
200        }
201    }
202    #[cfg(stabby_vtables = "vec")]
203    #[rustversion::all(not(nightly), since(1.78.0))]
204    pub(crate) static VTABLES: crate::alloc::sync::AtomicArc<
205        crate::alloc::vec::Vec<VTable>,
206        DefaultAllocator,
207    > = crate::alloc::sync::AtomicArc::new(None);
208    #[cfg(any(stabby_vtables = "btree", not(stabby_vtables)))]
209    #[rustversion::all(not(nightly), since(1.78.0))]
210    pub(crate) static VTABLES: AtomicArcBTreeSet<VTable, false, 5> = AtomicArcBTreeSet::new();
211    #[rustversion::nightly]
212    #[cfg(stabby_vtables = "btree")]
213    pub(crate) static VTABLES: AtomicArcBTreeSet<VTable, false, 5> = AtomicArcBTreeSet::new();
214}
215
216#[cfg(all(
217    not(stabby_default_alloc = "disabled"),
218    any(stabby_vtables = "vec", stabby_vtables = "btree", not(stabby_vtables))
219))]
220#[rustversion::all(not(nightly), since(1.78.0))]
221/// Implementation detail for stabby's version of dyn traits.
222/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
223pub trait IConstConstructor<'a, Source>: 'a + Copy {
224    /// The vtable.
225    const VTABLE: Self;
226    /// Returns the reference to the vtable
227    fn vtable() -> &'a Self {
228        use internal::VTableRegistry;
229        internal::VTABLES.insert_typed(&Self::VTABLE)
230    }
231}
232#[cfg(not(all(
233    not(stabby_default_alloc = "disabled"),
234    any(stabby_vtables = "vec", stabby_vtables = "btree", not(stabby_vtables))
235)))]
236#[rustversion::all(not(nightly), since(1.78.0))]
237/// Implementation detail for stabby's version of dyn traits.
238/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
239pub trait IConstConstructor<'a, Source>: 'a + Copy {
240    /// The vtable.
241    const VTABLE: Self;
242    /// Returns the reference to the vtable
243    fn vtable() -> &'a Self {
244        core::compile_error!("stabby's dyn traits need an allocator for non-nightly versions of Rust starting `1.78.0` and until https://github.com/rust-lang/rfcs/pull/3633 lands in stable.")
245    }
246}
247/// Implementation detail for stabby's version of dyn traits.
248pub trait TransitiveDeref<Head, N> {
249    /// Deref transitiverly.
250    fn tderef(&self) -> &Head;
251}
252/// Implementation detail for stabby's version of dyn traits.
253pub struct H;
254/// Implementation detail for stabby's version of dyn traits.
255pub struct T<T>(T);
256/// A recursive type to define sets of v-tables.
257/// You should _always_ use `stabby::vtable!(Trait1 + Trait2 + ...)` to generate this type,
258/// as this macro will ensure that traits are ordered consistently in the vtable.
259#[stabby::stabby]
260#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
261pub struct VTable<Head, Tail = VtDrop> {
262    /// The rest of the vtable.
263    ///
264    /// It comes first to allow upcasting vtables.
265    pub tail: Tail,
266    /// The head of the vtable (the last trait listed in the macros)
267    pub head: Head,
268}
269
270/// Concatenate vtables
271pub trait CompoundVt<'vt_lt> {
272    /// The concatenated vtable.
273    type Vt<T>;
274}
275
276impl<'a, T, Head: Copy + 'a, Tail: Copy + 'a> IConstConstructor<'a, T> for VTable<Head, Tail>
277where
278    Head: IConstConstructor<'a, T>,
279    Tail: IConstConstructor<'a, T>,
280{
281    impl_vtable_constructor!(
282        const VTABLE_REF: &'a VTable<Head, Tail> = &VTable {
283            head: *Head::VTABLE_REF,
284            tail: *Tail::VTABLE_REF,
285        }; =>
286        const VTABLE: VTable<Head, Tail> = VTable {
287            head: Head::VTABLE,
288            tail: Tail::VTABLE,
289        };
290    );
291}
292#[allow(clippy::needless_lifetimes)]
293impl<'a, T> IConstConstructor<'a, T> for () {
294    impl_vtable_constructor!(
295        const VTABLE_REF: &'a () = &();=>
296        const VTABLE: () = (););
297}
298impl<Head, Tail> TransitiveDeref<Head, H> for VTable<Head, Tail> {
299    fn tderef(&self) -> &Head {
300        &self.head
301    }
302}
303impl<Head, Tail: TransitiveDeref<Vt, N>, Vt, N> TransitiveDeref<Vt, T<N>> for VTable<Head, Tail> {
304    fn tderef(&self) -> &Vt {
305        self.tail.tderef()
306    }
307}
308
309/// Allows extracting the dropping vtable from a vtable
310pub trait HasDropVt {
311    /// Access the [`VtDrop`] section of a vtable.
312    fn drop_vt(&self) -> &VtDrop;
313}
314impl HasDropVt for VtDrop {
315    fn drop_vt(&self) -> &VtDrop {
316        self
317    }
318}
319impl<Head, Tail: HasDropVt> HasDropVt for VTable<Head, Tail> {
320    fn drop_vt(&self) -> &VtDrop {
321        self.tail.drop_vt()
322    }
323}
324impl<T: HasDropVt> HasDropVt for VtSend<T> {
325    fn drop_vt(&self) -> &VtDrop {
326        self.0.drop_vt()
327    }
328}
329impl<T: HasDropVt> HasDropVt for VtSync<T> {
330    fn drop_vt(&self) -> &VtDrop {
331        self.0.drop_vt()
332    }
333}
334
335/// Whether or not a vtable includes [`VtSend`]
336pub trait HasSendVt {}
337impl<T> HasSendVt for VtSend<T> {}
338impl<T: HasSendVt> HasSendVt for VtSync<T> {}
339impl<Head, Tail: HasSyncVt> HasSendVt for VTable<Head, Tail> {}
340/// Whether or not a vtable includes [`VtSync`]
341pub trait HasSyncVt {}
342impl<T> HasSyncVt for VtSync<T> {}
343impl<T: HasSyncVt> HasSyncVt for VtSend<T> {}
344impl<Head, Tail: HasSyncVt> HasSyncVt for VTable<Head, Tail> {}
345
346// DROP
347/// The vtable to drop a value in place
348#[stabby::stabby]
349#[derive(Clone, Copy, Eq)]
350pub struct VtDrop {
351    /// The [`Drop::drop`] function, shimmed with the C calling convention.
352    pub drop: crate::StableLike<unsafe extern "C" fn(AnonymRefMut<'_>), core::num::NonZeroUsize>,
353}
354impl core::fmt::Debug for VtDrop {
355    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
356        write!(f, "VtDrop({:p})", unsafe { self.drop.as_ref_unchecked() })
357    }
358}
359impl Hash for VtDrop {
360    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
361        self.drop.hash(state)
362    }
363}
364impl PartialEq for VtDrop {
365    fn eq(&self, other: &Self) -> bool {
366        core::ptr::eq(
367            unsafe { self.drop.as_ref_unchecked() }
368                as *const unsafe extern "C" fn(AnonymRefMut<'_>),
369            unsafe { other.drop.as_ref_unchecked() }
370                as *const unsafe extern "C" fn(AnonymRefMut<'_>),
371        )
372    }
373}
374unsafe extern "C" fn drop<T>(this: AnonymRefMut<'_>) {
375    core::ptr::drop_in_place(unsafe { this.cast::<T>().as_mut() })
376}
377#[allow(unknown_lints)]
378#[allow(clippy::missing_transmute_annotations, clippy::needless_lifetimes)]
379impl<'a, T> IConstConstructor<'a, T> for VtDrop {
380    impl_vtable_constructor!(
381        const VTABLE_REF: &'a VtDrop = &VtDrop {
382            drop: unsafe {
383                core::mem::transmute(drop::<T> as unsafe extern "C" fn(AnonymRefMut<'_>))
384            },
385        }; =>
386        const VTABLE: VtDrop = VtDrop {
387            drop: unsafe {
388                core::mem::transmute(drop::<T> as unsafe extern "C" fn(AnonymRefMut<'_>))
389            },
390        };
391    );
392}
393
394/// A marker for vtables for types that are `Send`
395#[stabby::stabby]
396#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
397pub struct VtSend<T>(pub T);
398impl<'a> CompoundVt<'a> for dyn Send {
399    type Vt<T> = VtSend<T>;
400}
401impl<Tail: TransitiveDeref<Vt, N>, Vt, N> TransitiveDeref<Vt, N> for VtSend<Tail> {
402    fn tderef(&self) -> &Vt {
403        self.0.tderef()
404    }
405}
406impl<Head, Tail> From<crate::vtable::VtSend<VTable<Head, Tail>>> for VTable<Head, Tail> {
407    fn from(value: VtSend<VTable<Head, Tail>>) -> Self {
408        value.0
409    }
410}
411impl<'a, T: Send, Vt: IConstConstructor<'a, T>> IConstConstructor<'a, T> for VtSend<Vt> {
412    impl_vtable_constructor!(
413        const VTABLE_REF: &'a VtSend<Vt> = &VtSend(*Vt::VTABLE_REF);=>
414        const VTABLE: VtSend<Vt> = VtSend(Vt::VTABLE);
415    );
416}
417
418/// A marker for vtables for types that are `Sync`
419#[stabby::stabby]
420#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
421pub struct VtSync<T>(pub T);
422impl<'a> CompoundVt<'a> for dyn Sync {
423    type Vt<T> = VtSync<T>;
424}
425impl<'a, T: Sync, Vt: IConstConstructor<'a, T>> IConstConstructor<'a, T> for VtSync<Vt> {
426    impl_vtable_constructor!(
427        const VTABLE_REF: &'a VtSync<Vt> = &VtSync(*Vt::VTABLE_REF);=>
428        const VTABLE: VtSync<Vt> = VtSync(Vt::VTABLE);
429    );
430}
431impl<Tail: TransitiveDeref<Vt, N>, Vt, N> TransitiveDeref<Vt, N> for VtSync<Tail> {
432    fn tderef(&self) -> &Vt {
433        self.0.tderef()
434    }
435}
436impl<Head, Tail> From<crate::vtable::VtSync<VtSend<VTable<Head, Tail>>>> for VTable<Head, Tail> {
437    fn from(value: VtSync<VtSend<VTable<Head, Tail>>>) -> Self {
438        value.0 .0
439    }
440}
441impl<Head, Tail> From<crate::vtable::VtSync<VtSend<VTable<Head, Tail>>>>
442    for VtSend<VTable<Head, Tail>>
443{
444    fn from(value: VtSync<VtSend<VTable<Head, Tail>>>) -> Self {
445        value.0
446    }
447}
448impl<Head, Tail> From<crate::vtable::VtSync<VTable<Head, Tail>>> for VTable<Head, Tail> {
449    fn from(value: VtSync<VTable<Head, Tail>>) -> Self {
450        value.0
451    }
452}
453
454/// An ABI-stable equivalent to [`core::any::Any`]
455#[stabby::stabby]
456pub trait Any {
457    /// The report of the type.
458    extern "C" fn report(&self) -> &'static crate::report::TypeReport;
459    /// The id of the type.
460    extern "C" fn id(&self) -> u64;
461}
462impl<T: crate::IStable> Any for T {
463    extern "C" fn report(&self) -> &'static crate::report::TypeReport {
464        Self::REPORT
465    }
466    extern "C" fn id(&self) -> u64 {
467        Self::ID
468    }
469}