stabby_abi/
fatptr.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 core::ptr::NonNull;
16
17use crate as stabby;
18use crate::vtable::*;
19
20/// An anonimized reference to a value.
21#[stabby::stabby]
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
23pub struct AnonymRef<'a> {
24    /// The address of the reference.
25    pub ptr: NonNull<()>,
26    /// A marker to indicate the lifetime of the reference.
27    pub _marker: core::marker::PhantomData<&'a ()>,
28}
29impl<T> From<&T> for AnonymRef<'_> {
30    fn from(value: &T) -> Self {
31        Self {
32            ptr: NonNull::from(value).cast(),
33            _marker: core::marker::PhantomData,
34        }
35    }
36}
37impl core::ops::Deref for AnonymRef<'_> {
38    type Target = NonNull<()>;
39    fn deref(&self) -> &Self::Target {
40        &self.ptr
41    }
42}
43
44/// An anonimized mutable reference to a value.
45#[stabby::stabby]
46#[derive(Debug, PartialEq, Eq, Hash)]
47pub struct AnonymRefMut<'a> {
48    /// The address of the reference.
49    pub ptr: NonNull<()>,
50    /// A marker to indicate the lifetime of the reference.
51    pub _marker: core::marker::PhantomData<&'a mut ()>,
52}
53impl<T> From<&mut T> for AnonymRefMut<'_> {
54    fn from(value: &mut T) -> Self {
55        Self {
56            ptr: NonNull::from(value).cast(),
57            _marker: core::marker::PhantomData,
58        }
59    }
60}
61impl core::ops::Deref for AnonymRefMut<'_> {
62    type Target = NonNull<()>;
63    fn deref(&self) -> &Self::Target {
64        &self.ptr
65    }
66}
67impl core::ops::DerefMut for AnonymRefMut<'_> {
68    fn deref_mut(&mut self) -> &mut Self::Target {
69        &mut self.ptr
70    }
71}
72impl IPtrOwned for AnonymRefMut<'_> {
73    fn drop(_: &mut core::mem::ManuallyDrop<Self>, _: unsafe extern "C" fn(AnonymRefMut<'_>)) {}
74}
75impl IPtr for AnonymRefMut<'_> {
76    unsafe fn as_ref(&self) -> AnonymRef<'_> {
77        AnonymRef {
78            ptr: self.ptr,
79            _marker: core::marker::PhantomData,
80        }
81    }
82}
83impl IPtrMut for AnonymRefMut<'_> {
84    unsafe fn as_mut(&mut self) -> AnonymRefMut<'_> {
85        AnonymRefMut {
86            ptr: self.ptr,
87            _marker: core::marker::PhantomData,
88        }
89    }
90}
91
92/// Indicates that `Self` can be used as a pointer in dynptrs.
93pub trait IPtr {
94    /// # Safety
95    /// This function implies an implicit cast of the reference
96    unsafe fn as_ref(&self) -> AnonymRef<'_>;
97}
98/// Indicates that `Self` can be used as an unconditionally mutable pointer in dynptrs.
99pub trait IPtrMut: IPtr {
100    /// # Safety
101    /// This function implies an implicit cast of the reference
102    unsafe fn as_mut(&mut self) -> AnonymRefMut<'_>;
103}
104/// Indicates that `Self` can be used as a conditionally mutable pointer in dynptrs.
105pub trait IPtrTryAsMut {
106    /// # Safety
107    /// This function implies an implicit cast of the reference
108    unsafe fn try_as_mut(&mut self) -> Option<AnonymRefMut<'_>>;
109}
110impl<T: IPtrMut> IPtrTryAsMut for T {
111    unsafe fn try_as_mut(&mut self) -> Option<AnonymRefMut<'_>> {
112        Some(self.into())
113    }
114}
115/// Provides drop support in dynptr for pointers that have at least partial ownership of their pointee.
116///
117/// `drop` is the drop function of the pointee.
118pub trait IPtrOwned {
119    /// Called instead of `Drop::drop` when the dynptr is dropped.
120    fn drop(this: &mut core::mem::ManuallyDrop<Self>, drop: unsafe extern "C" fn(AnonymRefMut<'_>));
121}
122/// Provides Clone support for smart pointers that allow it.
123pub trait IPtrClone: IPtrOwned {
124    /// Clone the pointer.
125    fn clone(this: &Self) -> Self;
126}
127impl<T> IPtr for &T {
128    unsafe fn as_ref(&self) -> AnonymRef<'_> {
129        self.into()
130    }
131}
132impl<T> IPtr for &mut T {
133    unsafe fn as_ref(&self) -> AnonymRef<'_> {
134        self.into()
135    }
136}
137impl<T> IPtrMut for &mut T {
138    unsafe fn as_mut(&mut self) -> AnonymRefMut<'_> {
139        self.into()
140    }
141}
142impl<T> IPtrOwned for &mut T {
143    fn drop(_: &mut core::mem::ManuallyDrop<Self>, _: unsafe extern "C" fn(AnonymRefMut<'_>)) {}
144}
145
146/// Used to turn a pointer into a dynamic pointer.
147pub trait IntoDyn {
148    /// A pointer type with type information discarded.
149    type Anonymized;
150    /// The original pointee.
151    type Target;
152    /// Drop the type information from the pointer.
153    fn anonimize(self) -> Self::Anonymized;
154}
155impl<'a, T> IntoDyn for &'a T {
156    type Anonymized = &'a ();
157    type Target = T;
158    fn anonimize(self) -> Self::Anonymized {
159        unsafe { core::mem::transmute(self) }
160    }
161}
162impl<'a, T> IntoDyn for &'a mut T {
163    type Anonymized = &'a mut ();
164    type Target = T;
165    fn anonimize(self) -> Self::Anonymized {
166        unsafe { core::mem::transmute(self) }
167    }
168}
169
170#[stabby::stabby]
171#[derive(Clone, Copy)]
172/// A stable `&'a dyn Traits`
173pub struct DynRef<'a, Vt: 'static> {
174    ptr: AnonymRef<'a>,
175    vtable: &'a Vt,
176    unsend: core::marker::PhantomData<*mut ()>,
177}
178
179impl<'a, Vt: Copy + 'a> DynRef<'a, Vt> {
180    /// Access the data pointer.
181    pub const fn ptr(&self) -> AnonymRef<'a> {
182        self.ptr
183    }
184    /// Access the vtable.
185    pub const fn vtable(&self) -> &Vt {
186        self.vtable
187    }
188
189    /// Allows casting a `dyn A + B` into `dyn A`.
190    ///
191    /// Note that you can only remove the outermost (rightmost in dyn syntax) trait at a time,
192    /// except `Send` and `Sync` that may both be kept, or both be removed.
193    pub fn into_super<Super>(self) -> Super
194    where
195        Self: IntoSuperTrait<Super>,
196    {
197        IntoSuperTrait::into_super(self)
198    }
199    /// Downcasts the reference based on vtable equality.
200    ///
201    /// This implies that this downcast will always yield `None` when attempting to downcast
202    /// values constructed accross an FFI.
203    ///
204    /// # Safety
205    /// This may have false positives if all of the following applies:
206    /// - `self` was built from `&U`, within the same FFI-boundary,
207    /// - `T` and `U` have identical implementations for all methods of the vtable,
208    /// - the compiler chose to merge these implementations, making `T` and `U` share
209    ///   their function pointers.
210    ///
211    /// While all of these factors aligning is unlikely, you should be aware of this if you
212    /// plan on using methods of `T` that wouldn't be valid for `U`.
213    pub unsafe fn downcast<T>(&self) -> Option<&T>
214    where
215        Vt: PartialEq + IConstConstructor<'a, T>,
216    {
217        (self.vtable == Vt::vtable()).then(|| unsafe { self.ptr.cast().as_ref() })
218    }
219    /// Downcasts the reference based on its reflection report.
220    pub fn stable_downcast<T: crate::IStable, Path>(&self) -> Option<&T>
221    where
222        Vt: TransitiveDeref<crate::vtable::StabbyVtableAny<'a>, Path>,
223    {
224        (self.report() == T::REPORT).then(|| unsafe { self.ptr.cast().as_ref() })
225    }
226}
227#[stabby::stabby]
228/// A stable trait object (or a stable `&mut dyn`)
229pub struct Dyn<'a, P: IPtrOwned + 'a, Vt: HasDropVt + 'static> {
230    pub(crate) ptr: core::mem::ManuallyDrop<P>,
231    pub(crate) vtable: &'static Vt,
232    pub(crate) unsend: core::marker::PhantomData<&'a P>,
233}
234
235/// Allows casting a `dyn A + B` into `dyn A`.
236pub trait IntoSuperTrait<Super> {
237    /// Cast `dyn A + B` into `dyn A`.
238    fn into_super(this: Self) -> Super;
239}
240macro_rules! impl_super {
241    ($from: ty, $to: ty, $($generics: tt)*) => {
242        impl<'a, P: IPtrOwned + 'a + Sized, $($generics)*> IntoSuperTrait<Dyn<'a, P, $to>> for Dyn<'a, P, $from>
243        {
244            fn into_super(this: Self) -> Dyn<'a, P, $to> {
245                let ptr = &this as *const _;
246                core::mem::forget(this);
247                unsafe { core::ptr::read(ptr as *const _) }
248            }
249        }
250        impl<'a,  $($generics)*> IntoSuperTrait<DynRef<'a, $to>> for DynRef<'a, $from>
251        {
252            fn into_super(this: Self) -> DynRef<'a, $to> {
253                let ptr = &this as *const _;
254                unsafe { core::ptr::read(ptr as *const _) }
255            }
256        }
257    };
258}
259impl_super!(VTable<Head, Tail>, Tail, Head, Tail: HasDropVt + 'static);
260impl_super!(VtSend<Vt>, Vt, Vt: HasDropVt + 'static);
261impl_super!(VtSync<Vt>, Vt, Vt: HasDropVt + 'static);
262impl_super!(VtSync<VtSend<Vt>>, Vt, Vt: HasDropVt + 'static);
263impl_super!(VtSend<VtSync<Vt>>, Vt, Vt: HasDropVt + 'static);
264impl_super!(VtSync<VtSend<Vt>>, VtSync<Vt>, Vt: HasDropVt + 'static);
265impl_super!(VtSend<VtSync<Vt>>, VtSend<Vt>, Vt: HasDropVt + 'static);
266impl_super!(VtSend<VTable<Head, Tail>>, Tail, Head, Tail: HasDropVt + 'static);
267impl_super!(VtSync<VTable<Head, Tail>>, Tail, Head, Tail: HasDropVt + 'static);
268impl_super!(VtSync<VtSend<VTable<Head, Tail>>>, Tail, Head, Tail: HasDropVt + 'static);
269impl_super!(VtSend<VtSync<VTable<Head, Tail>>>, Tail, Head, Tail: HasDropVt + 'static);
270impl_super!(VtSend<VTable<Head, Tail>>, VtSend<Tail>, Head, Tail: HasDropVt + 'static);
271impl_super!(VtSync<VTable<Head, Tail>>, VtSync<Tail>, Head, Tail: HasDropVt + 'static);
272impl_super!(VtSync<VtSend<VTable<Head, Tail>>>, VtSync<VtSend<Tail>>, Head, Tail: HasDropVt + 'static);
273impl_super!(VtSend<VtSync<VTable<Head, Tail>>>, VtSend<VtSync<Tail>>, Head, Tail: HasDropVt + 'static);
274
275impl<'a, P: IPtrOwned + IPtrClone, Vt: HasDropVt + 'a> Clone for Dyn<'a, P, Vt> {
276    fn clone(&self) -> Self {
277        Self {
278            ptr: core::mem::ManuallyDrop::new(IPtrClone::clone(&self.ptr)),
279            vtable: self.vtable,
280            unsend: self.unsend,
281        }
282    }
283}
284impl<'a, P: IPtrOwned + IPtr, Vt: HasDropVt + 'a> Dyn<'a, P, Vt> {
285    /// Access the data pointer immutably.
286    #[allow(clippy::missing_const_for_fn)]
287    pub fn ptr(&self) -> &P {
288        &self.ptr
289    }
290    /// Access the data pointer mutably.
291    #[allow(clippy::missing_const_for_fn)]
292    pub fn ptr_mut(&mut self) -> &mut P {
293        &mut self.ptr
294    }
295    /// Access the vtable.
296    pub const fn vtable(&self) -> &'a Vt {
297        self.vtable
298    }
299    /// Borrow into an ABI-stable `&dyn Traits`
300    pub fn as_ref(&self) -> DynRef<'_, Vt> {
301        DynRef {
302            ptr: unsafe { self.ptr.as_ref() },
303            vtable: self.vtable,
304            unsend: core::marker::PhantomData,
305        }
306    }
307    /// Borrow into an ABI-stable `&mut dyn Traits`
308    pub fn as_mut(&mut self) -> Dyn<AnonymRefMut<'_>, Vt>
309    where
310        P: IPtrMut,
311    {
312        Dyn {
313            ptr: unsafe { core::mem::ManuallyDrop::new(self.ptr.as_mut()) },
314            vtable: self.vtable,
315            unsend: core::marker::PhantomData,
316        }
317    }
318    /// Attempt to borrow into an ABI-stable `&mut dyn Traits`
319    pub fn try_as_mut(&mut self) -> Option<Dyn<AnonymRefMut<'_>, Vt>>
320    where
321        P: IPtrTryAsMut,
322    {
323        Some(Dyn {
324            ptr: unsafe { core::mem::ManuallyDrop::new(self.ptr.try_as_mut()?) },
325            vtable: self.vtable,
326            unsend: core::marker::PhantomData,
327        })
328    }
329
330    /// Allows casting a `dyn A + B` into `dyn A`.
331    ///
332    /// Note that you can only remove the outermost (rightmost in dyn syntax) trait at a time,
333    /// except `Send` and `Sync` that may both be kept, or both be removed.
334    pub fn into_super<Super>(self) -> Super
335    where
336        Self: IntoSuperTrait<Super>,
337    {
338        IntoSuperTrait::into_super(self)
339    }
340
341    /// Downcasts the reference based on vtable equality.
342    ///
343    /// This implies that this downcast will always yield `None` when attempting to downcast
344    /// values constructed accross an FFI.
345    ///
346    /// Note that the compiler may chose to have multiple copies of the vtable, notably in optimized builds.
347    /// This means that even within a same compile unit, this function may fail to downcast a value even if
348    /// the type should have matched.
349    ///
350    /// In general, you should prefer [`Self::stable_downcast_ref`]
351    ///
352    /// # Safety
353    /// This may have false positives if all of the following applies:
354    /// - `self` was built from `&U`, within the same FFI-boundary,
355    /// - `T` and `U` have identical implementations for all methods of the vtable,
356    /// - the compiler chose to merge these implementations, making `T` and `U` share
357    ///   their function pointers.
358    ///
359    /// While all of these factors aligning is unlikely, you should be aware of this if you
360    /// plan on using methods of `T` that wouldn't be valid for `U`.
361    pub unsafe fn downcast_ref<T>(&self) -> Option<&T>
362    where
363        Vt: PartialEq + Copy + IConstConstructor<'a, T>,
364    {
365        (self.vtable == Vt::vtable()).then(|| unsafe { self.ptr.as_ref().cast::<T>().as_ref() })
366    }
367    /// Downcasts the reference based on its reflection report.
368    pub fn stable_downcast_ref<T: crate::IStable, Path>(&self) -> Option<&T>
369    where
370        Vt: TransitiveDeref<crate::vtable::StabbyVtableAny<'a>, Path> + IConstConstructor<'a, T>,
371    {
372        (self.id() == T::ID && self.report() == T::REPORT)
373            .then(|| unsafe { self.ptr.as_ref().cast::<T>().as_ref() })
374    }
375    /// Downcasts the mutable reference based on vtable equality.
376    ///
377    /// This implies that this downcast will always yield `None` when attempting to downcast
378    /// values constructed accross an FFI.
379    ///
380    /// Note that the compiler may chose to have multiple copies of the vtable, notably in optimized builds.
381    /// This means that even within a same compile unit, this function may fail to downcast a value even if
382    /// the type should have matched.
383    ///
384    /// In general, you should prefer [`Self::stable_downcast_mut`]
385    ///
386    /// # Safety
387    /// This may have false positives if all of the following applies:
388    /// - `self` was built from `&U`, within the same FFI-boundary,
389    /// - `T` and `U` have identical implementations for all methods of the vtable,
390    /// - the compiler chose to merge these implementations, making `T` and `U` share
391    ///   their function pointers.
392    ///
393    /// While all of these factors aligning is unlikely, you should be aware of this if you
394    /// plan on using methods of `T` that wouldn't be valid for `U`.
395    pub unsafe fn downcast_mut<T>(&mut self) -> Option<&mut T>
396    where
397        Vt: PartialEq + Copy + IConstConstructor<'a, T>,
398        P: IPtrMut,
399    {
400        (self.vtable == Vt::vtable()).then(|| unsafe { self.ptr.as_mut().cast::<T>().as_mut() })
401    }
402    /// Downcasts the mutable reference based on its reflection report.
403    pub fn stable_downcast_mut<T: crate::IStable, Path>(&mut self) -> Option<&mut T>
404    where
405        Vt: TransitiveDeref<crate::vtable::StabbyVtableAny<'a>, Path> + IConstConstructor<'a, T>,
406        P: IPtrMut,
407    {
408        (self.id() == T::ID && self.report() == T::REPORT)
409            .then(|| unsafe { self.ptr.as_mut().cast::<T>().as_mut() })
410    }
411}
412
413impl<
414        'a,
415        Vt: HasDropVt + Copy + IConstConstructor<'static, P::Target> + 'static,
416        P: IntoDyn + 'a,
417    > From<P> for Dyn<'a, P::Anonymized, Vt>
418where
419    P::Anonymized: IPtrOwned,
420{
421    fn from(value: P) -> Self {
422        Self {
423            ptr: core::mem::ManuallyDrop::new(value.anonimize()),
424            vtable: Vt::vtable(),
425            unsend: core::marker::PhantomData,
426        }
427    }
428}
429
430impl<P: IPtrOwned, Vt: HasDropVt> Drop for Dyn<'_, P, Vt> {
431    fn drop(&mut self) {
432        P::drop(&mut self.ptr, *unsafe {
433            self.vtable.drop_vt().drop.as_ref_unchecked()
434        })
435    }
436}
437
438impl<'a, T, Vt: Copy + IConstConstructor<'a, T>> From<&'a T> for DynRef<'a, Vt> {
439    fn from(value: &'a T) -> Self {
440        DynRef {
441            ptr: value.into(),
442            vtable: Vt::vtable(),
443            unsend: core::marker::PhantomData,
444        }
445    }
446}
447
448// SAFETY: This is analogous to a reference, and Vt proves the rest
449unsafe impl<Vt: HasSendVt> Send for DynRef<'_, Vt> {}
450// SAFETY: This is analogous to a reference, and Vt proves the rest
451unsafe impl<Vt: HasSyncVt> Sync for DynRef<'_, Vt> {}
452
453// SAFETY: The pointer must be `Send` and the pointee must me `Send` and `Sync`.
454unsafe impl<P: IPtrOwned + Send, Vt: HasSendVt + HasDropVt> Send for Dyn<'_, P, Vt> {}
455// SAFETY: The pointer must be `Sync` and the pointee must me `Send` and `Sync`.
456unsafe impl<P: IPtrOwned + Sync, Vt: HasSyncVt + HasDropVt> Sync for Dyn<'_, P, Vt> {}