Skip to main content

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>().div_euclid(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>().div_euclid(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
111                        .get(search_start..)
112                        .unwrap_or(&[])
113                        .iter()
114                        .find(|e| **e == vtable)
115                    {
116                        Some(vt) => {
117                            // SAFETY: since `vt.0` is a reference, it is guaranteed to be non-null.
118                            return unsafe { NonNull::new_unchecked(vt.0.as_ptr().cast_mut()) };
119                        }
120                        None => vts,
121                    },
122                };
123                let avt = allocated_slice.unwrap_or_else(|| BoxedSlice::from(vtable));
124                // SAFETY::`avt` will be leaked if inserting `vt` succeeds.
125                let vt = unsafe { core::mem::transmute::<&[_], &'static [_]>(avt.as_slice()) };
126                allocated_slice = Some(avt);
127                if let Err(updated) =
128                    self.is(vtables.as_ref(), core::sync::atomic::Ordering::SeqCst)
129                {
130                    vtables = updated;
131                    continue;
132                }
133                let mut vec = Vec::with_capacity(vts.len().wrapping_add(1));
134                vec.copy_extend(vts);
135                vec.push(VTable(vt));
136                if let Err(updated) =
137                    self.is(vtables.as_ref(), core::sync::atomic::Ordering::SeqCst)
138                {
139                    vtables = updated;
140                    continue;
141                }
142                let vec = Some(crate::alloc::sync::Arc::new(vec));
143                match self.compare_exchange(
144                    vtables.as_ref(),
145                    vec,
146                    core::sync::atomic::Ordering::SeqCst,
147                    core::sync::atomic::Ordering::SeqCst,
148                ) {
149                    Ok(_) => {
150                        core::mem::forget(allocated_slice);
151                        return unsafe { NonNull::new_unchecked(vt.as_ptr().cast_mut()) };
152                    }
153                    Err(new_vtables) => {
154                        search_start = vts.len();
155                        vtables = new_vtables;
156                    }
157                }
158            }
159        }
160    }
161    #[allow(unknown_lints)]
162    #[allow(clippy::missing_transmute_annotations)]
163    impl<const SIZE: usize> VTableRegistry for VtBtree<SIZE> {
164        fn insert(&self, vtable: &[*const ()]) -> NonNull<*const ()> {
165            let mut ret = None;
166            let mut allocated_slice: Option<BoxedSlice<_, DefaultAllocator>> = None;
167            self.edit(|tree| {
168                let mut tree = tree.clone();
169                if let Some(vt) = tree.get(&vtable) {
170                    ret = unsafe { Some(NonNull::new_unchecked(vt.0.as_ptr().cast_mut())) };
171                    return tree;
172                }
173                let vt = match &allocated_slice {
174                    Some(vt) => unsafe { core::mem::transmute(vt.as_slice()) },
175                    None => {
176                        let vt = BoxedSlice::from(vtable);
177                        let slice = unsafe { core::mem::transmute(vt.as_slice()) };
178                        allocated_slice = Some(vt);
179                        slice
180                    }
181                };
182                tree.insert(vt);
183                tree
184            });
185            if ret.is_none() {
186                let ret = &mut ret;
187                self.get(&vtable, move |vt| {
188                    let start = unsafe {
189                        NonNull::new_unchecked(
190                            vt.expect("VTable should've been inserted by now")
191                                .0
192                                .as_ptr()
193                                .cast_mut(),
194                        )
195                    };
196                    if let Some(allocated) = allocated_slice {
197                        if allocated.slice.start.ptr == start {
198                            core::mem::forget(allocated);
199                        }
200                    }
201                    *ret = Some(start);
202                });
203            }
204            unsafe { ret.unwrap_unchecked() }
205        }
206    }
207    #[cfg(stabby_vtables = "vec")]
208    #[rustversion::all(not(nightly), since(1.78.0))]
209    pub(crate) static VTABLES: crate::alloc::sync::AtomicArc<
210        crate::alloc::vec::Vec<VTable>,
211        DefaultAllocator,
212    > = crate::alloc::sync::AtomicArc::new(None);
213    #[cfg(any(stabby_vtables = "btree", not(stabby_vtables)))]
214    #[rustversion::all(not(nightly), since(1.78.0))]
215    pub(crate) static VTABLES: AtomicArcBTreeSet<VTable, false, 5> = AtomicArcBTreeSet::new();
216    #[rustversion::nightly]
217    #[cfg(stabby_vtables = "btree")]
218    pub(crate) static VTABLES: AtomicArcBTreeSet<VTable, false, 5> = AtomicArcBTreeSet::new();
219}
220
221#[cfg(all(
222    not(stabby_default_alloc = "disabled"),
223    any(stabby_vtables = "vec", stabby_vtables = "btree", not(stabby_vtables))
224))]
225#[rustversion::all(not(nightly), since(1.78.0))]
226/// Implementation detail for stabby's version of dyn traits.
227/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
228pub trait IConstConstructor<'a, Source>: 'a + Copy {
229    /// The vtable.
230    const VTABLE: Self;
231    /// Returns the reference to the vtable
232    fn vtable() -> &'a Self {
233        use internal::VTableRegistry;
234        internal::VTABLES.insert_typed(&Self::VTABLE)
235    }
236}
237#[cfg(not(all(
238    not(stabby_default_alloc = "disabled"),
239    any(stabby_vtables = "vec", stabby_vtables = "btree", not(stabby_vtables))
240)))]
241#[rustversion::all(not(nightly), since(1.78.0))]
242/// Implementation detail for stabby's version of dyn traits.
243/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
244pub trait IConstConstructor<'a, Source>: 'a + Copy {
245    /// The vtable.
246    const VTABLE: Self;
247    /// Returns the reference to the vtable
248    fn vtable() -> &'a Self {
249        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.")
250    }
251}
252/// Implementation detail for stabby's version of dyn traits.
253pub trait TransitiveDeref<Head, N> {
254    /// Deref transitiverly.
255    fn tderef(&self) -> &Head;
256}
257/// Implementation detail for stabby's version of dyn traits.
258pub struct H;
259/// Implementation detail for stabby's version of dyn traits.
260pub struct T<T>(T);
261/// A recursive type to define sets of v-tables.
262/// You should _always_ use `stabby::vtable!(Trait1 + Trait2 + ...)` to generate this type,
263/// as this macro will ensure that traits are ordered consistently in the vtable.
264#[stabby::stabby]
265#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
266pub struct VTable<Head, Tail = VtDrop> {
267    /// The rest of the vtable.
268    ///
269    /// It comes first to allow upcasting vtables.
270    pub tail: Tail,
271    /// The head of the vtable (the last trait listed in the macros)
272    pub head: Head,
273}
274
275/// Concatenate vtables
276pub trait CompoundVt<'vt_lt> {
277    /// The concatenated vtable.
278    type Vt<T>;
279}
280
281impl<'a, T, Head: Copy + 'a, Tail: Copy + 'a> IConstConstructor<'a, T> for VTable<Head, Tail>
282where
283    Head: IConstConstructor<'a, T>,
284    Tail: IConstConstructor<'a, T>,
285{
286    impl_vtable_constructor!(
287        const VTABLE_REF: &'a VTable<Head, Tail> = &VTable {
288            head: *Head::VTABLE_REF,
289            tail: *Tail::VTABLE_REF,
290        }; =>
291        const VTABLE: VTable<Head, Tail> = VTable {
292            head: Head::VTABLE,
293            tail: Tail::VTABLE,
294        };
295    );
296}
297#[allow(clippy::needless_lifetimes)]
298impl<'a, T> IConstConstructor<'a, T> for () {
299    impl_vtable_constructor!(
300        const VTABLE_REF: &'a () = &();=>
301        const VTABLE: () = (););
302}
303impl<Head, Tail> TransitiveDeref<Head, H> for VTable<Head, Tail> {
304    fn tderef(&self) -> &Head {
305        &self.head
306    }
307}
308impl<Head, Tail: TransitiveDeref<Vt, N>, Vt, N> TransitiveDeref<Vt, T<N>> for VTable<Head, Tail> {
309    fn tderef(&self) -> &Vt {
310        self.tail.tderef()
311    }
312}
313
314/// Allows extracting the dropping vtable from a vtable
315pub trait HasDropVt {
316    /// Access the [`VtDrop`] section of a vtable.
317    fn drop_vt(&self) -> &VtDrop;
318}
319impl HasDropVt for VtDrop {
320    fn drop_vt(&self) -> &VtDrop {
321        self
322    }
323}
324impl<Head, Tail: HasDropVt> HasDropVt for VTable<Head, Tail> {
325    fn drop_vt(&self) -> &VtDrop {
326        self.tail.drop_vt()
327    }
328}
329impl<T: HasDropVt> HasDropVt for VtSend<T> {
330    fn drop_vt(&self) -> &VtDrop {
331        self.0.drop_vt()
332    }
333}
334impl<T: HasDropVt> HasDropVt for VtSync<T> {
335    fn drop_vt(&self) -> &VtDrop {
336        self.0.drop_vt()
337    }
338}
339
340/// Whether or not a vtable includes [`VtSend`]
341pub trait HasSendVt {}
342impl<T> HasSendVt for VtSend<T> {}
343impl<T: HasSendVt> HasSendVt for VtSync<T> {}
344impl<Head, Tail: HasSyncVt> HasSendVt for VTable<Head, Tail> {}
345/// Whether or not a vtable includes [`VtSync`]
346pub trait HasSyncVt {}
347impl<T> HasSyncVt for VtSync<T> {}
348impl<T: HasSyncVt> HasSyncVt for VtSend<T> {}
349impl<Head, Tail: HasSyncVt> HasSyncVt for VTable<Head, Tail> {}
350
351// DROP
352/// The vtable to drop a value in place
353#[stabby::stabby]
354#[derive(Clone, Copy, Eq)]
355pub struct VtDrop {
356    /// The [`Drop::drop`] function, shimmed with the C calling convention.
357    pub drop: crate::StableLike<unsafe extern "C" fn(AnonymRefMut<'_>), core::num::NonZeroUsize>,
358}
359impl core::fmt::Debug for VtDrop {
360    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
361        write!(f, "VtDrop({:p})", unsafe { self.drop.as_ref_unchecked() })
362    }
363}
364impl Hash for VtDrop {
365    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
366        self.drop.hash(state)
367    }
368}
369impl PartialEq for VtDrop {
370    fn eq(&self, other: &Self) -> bool {
371        core::ptr::eq(
372            unsafe { self.drop.as_ref_unchecked() }
373                as *const unsafe extern "C" fn(AnonymRefMut<'_>),
374            unsafe { other.drop.as_ref_unchecked() }
375                as *const unsafe extern "C" fn(AnonymRefMut<'_>),
376        )
377    }
378}
379unsafe extern "C" fn drop<T>(this: AnonymRefMut<'_>) {
380    core::ptr::drop_in_place(unsafe { this.cast::<T>().as_mut() })
381}
382#[allow(unknown_lints)]
383#[allow(clippy::missing_transmute_annotations, clippy::needless_lifetimes)]
384impl<'a, T> IConstConstructor<'a, T> for VtDrop {
385    impl_vtable_constructor!(
386        const VTABLE_REF: &'a VtDrop = &VtDrop {
387            drop: unsafe {
388                core::mem::transmute(drop::<T> as unsafe extern "C" fn(AnonymRefMut<'_>))
389            },
390        }; =>
391        const VTABLE: VtDrop = VtDrop {
392            drop: unsafe {
393                core::mem::transmute(drop::<T> as unsafe extern "C" fn(AnonymRefMut<'_>))
394            },
395        };
396    );
397}
398
399/// A marker for vtables for types that are `Send`
400#[stabby::stabby]
401#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
402pub struct VtSend<T>(pub T);
403impl<'a> CompoundVt<'a> for dyn Send {
404    type Vt<T> = VtSend<T>;
405}
406impl<Tail: TransitiveDeref<Vt, N>, Vt, N> TransitiveDeref<Vt, N> for VtSend<Tail> {
407    fn tderef(&self) -> &Vt {
408        self.0.tderef()
409    }
410}
411impl<Head, Tail> From<crate::vtable::VtSend<VTable<Head, Tail>>> for VTable<Head, Tail> {
412    fn from(value: VtSend<VTable<Head, Tail>>) -> Self {
413        value.0
414    }
415}
416impl<'a, T: Send, Vt: IConstConstructor<'a, T>> IConstConstructor<'a, T> for VtSend<Vt> {
417    impl_vtable_constructor!(
418        const VTABLE_REF: &'a VtSend<Vt> = &VtSend(*Vt::VTABLE_REF);=>
419        const VTABLE: VtSend<Vt> = VtSend(Vt::VTABLE);
420    );
421}
422
423/// A marker for vtables for types that are `Sync`
424#[stabby::stabby]
425#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
426pub struct VtSync<T>(pub T);
427impl<'a> CompoundVt<'a> for dyn Sync {
428    type Vt<T> = VtSync<T>;
429}
430impl<'a, T: Sync, Vt: IConstConstructor<'a, T>> IConstConstructor<'a, T> for VtSync<Vt> {
431    impl_vtable_constructor!(
432        const VTABLE_REF: &'a VtSync<Vt> = &VtSync(*Vt::VTABLE_REF);=>
433        const VTABLE: VtSync<Vt> = VtSync(Vt::VTABLE);
434    );
435}
436impl<Tail: TransitiveDeref<Vt, N>, Vt, N> TransitiveDeref<Vt, N> for VtSync<Tail> {
437    fn tderef(&self) -> &Vt {
438        self.0.tderef()
439    }
440}
441impl<Head, Tail> From<crate::vtable::VtSync<VtSend<VTable<Head, Tail>>>> for VTable<Head, Tail> {
442    fn from(value: VtSync<VtSend<VTable<Head, Tail>>>) -> Self {
443        value.0 .0
444    }
445}
446impl<Head, Tail> From<crate::vtable::VtSync<VtSend<VTable<Head, Tail>>>>
447    for VtSend<VTable<Head, Tail>>
448{
449    fn from(value: VtSync<VtSend<VTable<Head, Tail>>>) -> Self {
450        value.0
451    }
452}
453impl<Head, Tail> From<crate::vtable::VtSync<VTable<Head, Tail>>> for VTable<Head, Tail> {
454    fn from(value: VtSync<VTable<Head, Tail>>) -> Self {
455        value.0
456    }
457}
458
459/// An ABI-stable equivalent to [`core::any::Any`]
460#[stabby::stabby]
461pub trait Any {
462    /// The report of the type.
463    extern "C" fn report(&self) -> &'static crate::report::TypeReport;
464    /// The id of the type.
465    extern "C" fn id(&self) -> u64;
466}
467impl<T: crate::IStable> Any for T {
468    extern "C" fn report(&self) -> &'static crate::report::TypeReport {
469        Self::REPORT
470    }
471    extern "C" fn id(&self) -> u64 {
472        Self::ID
473    }
474}