stabby_abi/
lib.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
15//! The core of the [`stabby`](https://crates.io/crates/stabby) ABI.
16//!
17//! This crate is generally not meant to be used directly, but through the `stabby` crate.
18
19#![deny(
20    missing_docs,
21    clippy::missing_panics_doc,
22    clippy::missing_const_for_fn,
23    clippy::missing_safety_doc,
24    clippy::missing_errors_doc,
25    // clippy::undocumented_unsafe_blocks
26)]
27#![cfg_attr(not(feature = "std"), no_std)]
28#![cfg_attr(stabby_nightly, feature(freeze))]
29
30#[cfg(feature = "alloc-rs")]
31extern crate alloc as alloc_rs;
32
33/// ABI-stable smart pointers and allocated data structures, with support for custom allocators.
34pub mod alloc;
35/// Extending [Non-Zero Types](core::num) to enable niches for other values than 0.
36pub mod num;
37
38pub use stabby_macros::{canary_suffixes, dynptr, export, import, stabby, vtable as vtmacro};
39use typenum2::unsigned::Alignment;
40
41use core::fmt::{Debug, Display};
42
43/// A no-op that fails to compile if `T` isn't proven ABI-stable by stabby.
44pub const fn assert_stable<T: IStable>() {}
45
46/// An ABI-stable tuple.
47pub use tuple::Tuple2 as Tuple;
48
49/// Generate the [`IStable::REPORT`] and [`IStable::ID`] fields for an implementation of [`IStable`].
50#[macro_export]
51macro_rules! primitive_report {
52    ($name: expr, $ty: ty) => {
53        const REPORT: &'static $crate::report::TypeReport = &$crate::report::TypeReport {
54            name: $crate::str::Str::new($name),
55            module: $crate::str::Str::new(core::module_path!()),
56            fields: $crate::StableLike::new(Some(&$crate::report::FieldReport {
57                name: $crate::str::Str::new("inner"),
58                ty: <$ty as $crate::IStable>::REPORT,
59                next_field: $crate::StableLike::new(None),
60            })),
61            version: 0,
62            tyty: $crate::report::TyTy::Struct,
63        };
64        const ID: u64 = $crate::report::gen_id(Self::REPORT);
65    };
66    ($name: expr) => {
67        const REPORT: &'static $crate::report::TypeReport = &$crate::report::TypeReport {
68            name: $crate::str::Str::new($name),
69            module: $crate::str::Str::new(core::module_path!()),
70            fields: $crate::StableLike::new(None),
71            version: 0,
72            tyty: $crate::report::TyTy::Struct,
73        };
74        const ID: u64 = $crate::report::gen_id(Self::REPORT);
75    };
76}
77
78/// A support module for stabby's dark magic.
79///
80/// It implements basic arithmetics in the type system, and needs to be included in stabby for the ternaries
81/// to keep trait bounds that are needed for proofs to work out.
82pub mod typenum2;
83use istable::{ISaturatingAdd, Saturator};
84#[doc(hidden)]
85pub use typenum2::*;
86
87/// A re-export of `rustversion` used in macros for dark magic.
88///
89/// Its API is subject to un-anounced changes.
90pub use rustversion as __rustversion;
91
92/// A support macro for stabby's dark magic.
93///
94/// Its API is subject to un-anounced changes.
95#[macro_export]
96macro_rules! impl_vtable_constructor {
97    ($pre178: item => $post178: item) => {
98        #[$crate::__rustversion::before(1.78.0)]
99        $pre178
100        #[$crate::__rustversion::since(1.78.0)]
101        $post178
102    };
103}
104
105/// Fires a compile error if the layout of a type is deemed sub-optimal.
106#[macro_export]
107macro_rules! assert_optimal_layout {
108    ($t: ty) => {
109        const _: () = {
110            assert!(<$t>::has_optimal_layout());
111        };
112    };
113}
114pub use crate::enums::IDeterminantProvider;
115/// Helpers to treat ABI-stable types as if they were their unstable equivalents.
116pub mod as_mut;
117/// ABI-stable equivalents of iterators.
118pub mod iter;
119
120/// Provides access to a value _as if_ it were of another type.
121///
122/// This is done by the following process:
123/// - memcopy `self` into `copy`
124/// - convert `copy` into `target: ManuallyDrop<Target>`
125/// - provide a guard that can `Deref` or `DerefMut` into `target`
126/// - upon dropping the mutable guard, convert `target` and assing `target` to `self`
127///
128/// This is always safe for non-self-referencial types.
129pub trait AccessAs {
130    /// Provides immutable access to a type as if it were its ABI-unstable equivalent.
131    fn ref_as<T: ?Sized>(&self) -> <Self as as_mut::IGuardRef<T>>::Guard<'_>
132    where
133        Self: as_mut::IGuardRef<T>;
134    /// Provides mutable access to a type as if it were its ABI-unstable equivalent.
135    fn mut_as<T: ?Sized>(&mut self) -> <Self as as_mut::IGuardMut<T>>::GuardMut<'_>
136    where
137        Self: as_mut::IGuardMut<T>;
138}
139
140pub use fatptr::*;
141/// How stabby does multi-trait objects.
142mod fatptr;
143
144/// Closures, but ABI-stable
145pub mod closure;
146/// Futures, but ABI-stable
147pub mod future;
148mod stable_impls;
149/// Support for vtables for multi-trait objects
150pub mod vtable;
151
152// #[allow(type_alias_bounds)]
153// pub type Stable<Source: IStabilize> = Source::Stable;
154
155/// A ZST that's only allowed to exist if its generic parameter is ABI-stable.
156pub struct AssertStable<T: IStable>(pub core::marker::PhantomData<T>);
157impl<T: IStable> AssertStable<T> {
158    /// Proves that `T` is ABI-stable.
159    pub const fn assert() -> Self {
160        Self(core::marker::PhantomData)
161    }
162}
163
164/// Lets you tell `stabby` that `T` has the same stable layout as `As`.
165///
166/// Lying about this link between `T` and `As` will cause UB if a `#[repr(stabby)]` enum transitively contains
167/// a value of this type.
168///
169/// If you want to be safe when using this, use [`NoNiches`] with the correct size and alignment for your
170/// type.
171#[repr(C)]
172#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
173pub struct StableLike<T, As> {
174    value: T,
175    marker: core::marker::PhantomData<As>,
176}
177impl<T: Debug, As> Debug for StableLike<T, As> {
178    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
179        self.value.fmt(f)
180    }
181}
182impl<T: Display, As> Display for StableLike<T, As> {
183    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
184        self.value.fmt(f)
185    }
186}
187impl<T: Clone, As> Clone for StableLike<T, As> {
188    fn clone(&self) -> Self {
189        Self {
190            value: self.value.clone(),
191            marker: self.marker,
192        }
193    }
194}
195impl<T: Copy, As> Copy for StableLike<T, As> {}
196trait ConstChecks {
197    const CHECK: ();
198}
199impl<T, As: IStable> ConstChecks for StableLike<T, As> {
200    const CHECK: () = {
201        if core::mem::size_of::<T>() != <As::Size as Unsigned>::USIZE {
202            panic!(
203                "Attempted to construct `StableLike<T, As>` despite As::Size not matching T's size"
204            )
205        }
206        if core::mem::align_of::<T>() != <As::Align as Unsigned>::USIZE {
207            panic!(
208                "Attempted to construct `StableLike<T, As>` despite As::Size not matching T's size"
209            )
210        }
211    };
212}
213impl<T, As: IStable> StableLike<T, As> {
214    /// Wraps a value in a type that provides information about its layout.
215    ///
216    /// Asserts that `T` and `As` have the same size and aligment at compile time,
217    /// and relies on the user for the niche information to be correct.
218    #[allow(clippy::let_unit_value)]
219    pub const fn new(value: T) -> Self {
220        _ = Self::CHECK;
221        Self {
222            value,
223            marker: core::marker::PhantomData,
224        }
225    }
226    /// Returns a reference to the underlying type
227    /// # Safety
228    /// This is only safe if `T` is FFI-safe, or if this `self` was constructed from a value
229    /// of `T` that was instanciated within the same shared object.
230    pub const unsafe fn as_ref_unchecked(&self) -> &T {
231        &self.value
232    }
233    /// Returns a reference to the underlying type
234    pub const fn as_ref(&self) -> &T
235    where
236        T: IStable,
237    {
238        &self.value
239    }
240    /// # Safety
241    /// This is only safe if `T` is FFI-safe, or if this `self` was constructed from a value
242    /// of `T` that was instanciated within the same shared object.
243    pub unsafe fn as_mut_unchecked(&mut self) -> &mut T {
244        &mut self.value
245    }
246    /// # Safety
247    /// This is only safe if `T` is FFI-safe, or if this `self` was constructed from a value
248    /// of `T` that was instanciated within the same shared object.
249    pub unsafe fn into_inner_unchecked(self) -> T {
250        self.value
251    }
252    /// Extracts the inner value from `self`
253    pub fn into_inner(self) -> T
254    where
255        T: IStable,
256    {
257        self.value
258    }
259}
260
261unsafe impl<T, As: IStable> IStable for StableLike<T, As> {
262    type Size = As::Size;
263    type Align = As::Align;
264    type ForbiddenValues = As::ForbiddenValues;
265    type UnusedBits = As::UnusedBits;
266    type HasExactlyOneNiche = As::HasExactlyOneNiche;
267    type ContainsIndirections = As::ContainsIndirections;
268    #[cfg(feature = "experimental-ctypes")]
269    type CType = As::CType;
270    const ID: u64 = crate::report::gen_id(Self::REPORT);
271    const REPORT: &'static report::TypeReport = As::REPORT;
272}
273
274/// Emulates a type of size `Size` and alignment `Align`.
275///
276/// Note that this is not a ZST, and that you may pass [`B0`] or [`B1`] as the this generic parameter if you
277/// want to inform `stabby` that the type it emulates has exactly zero or one niche respectively that the
278/// compiler knows about. This information can be used by `stabby` to determine that `core::option::Option`s
279/// transitively containing the emulated type are indeed ABI-stable.
280pub struct NoNiches<
281    Size: Unsigned,
282    Align: Alignment,
283    HasExactlyOneNiche: ISaturatingAdd = Saturator,
284    ContainsIndirections: Bit = B0,
285>(
286    Size::Padding,
287    core::marker::PhantomData<(Size, Align, HasExactlyOneNiche, ContainsIndirections)>,
288);
289unsafe impl<
290        Size: Unsigned,
291        Align: Alignment,
292        HasExactlyOneNiche: ISaturatingAdd,
293        ContainsIndirections: Bit,
294    > IStable for NoNiches<Size, Align, HasExactlyOneNiche, ContainsIndirections>
295{
296    type Size = Size;
297    type Align = Align;
298    type ForbiddenValues = End;
299    type UnusedBits = End;
300    type HasExactlyOneNiche = HasExactlyOneNiche;
301    type ContainsIndirections = ContainsIndirections;
302    #[cfg(feature = "experimental-ctypes")]
303    type CType = ();
304    primitive_report!("NoNiches");
305}
306
307/// Allows removing the [`IStable`] implementation from `T` if `Cond` is not also ABI-stable.
308///
309/// This is typically used in combination with [`StableLike`], for example in vtables to mark function
310/// pointers as stable only if all of their arguments are stable.
311#[repr(C)]
312pub struct StableIf<T, Cond> {
313    /// The actual value
314    pub value: T,
315    marker: core::marker::PhantomData<Cond>,
316}
317impl<T: Clone, Cond> Clone for StableIf<T, Cond> {
318    fn clone(&self) -> Self {
319        Self {
320            value: self.value.clone(),
321            marker: self.marker,
322        }
323    }
324}
325impl<T: Copy, Cond> Copy for StableIf<T, Cond> {}
326impl<T, Cond> StableIf<T, Cond> {
327    /// # Safety
328    /// Refer to type documentation
329    pub const unsafe fn new(value: T) -> Self {
330        Self {
331            value,
332            marker: core::marker::PhantomData,
333        }
334    }
335}
336
337impl<T, Cond> core::ops::Deref for StableIf<T, Cond> {
338    type Target = T;
339    fn deref(&self) -> &Self::Target {
340        &self.value
341    }
342}
343impl<T, Cond> core::ops::DerefMut for StableIf<T, Cond> {
344    fn deref_mut(&mut self) -> &mut Self::Target {
345        &mut self.value
346    }
347}
348unsafe impl<T: IStable, Cond: IStable> IStable for StableIf<T, Cond> {
349    type Size = T::Size;
350    type Align = T::Align;
351    type ForbiddenValues = T::ForbiddenValues;
352    type UnusedBits = T::UnusedBits;
353    type HasExactlyOneNiche = T::HasExactlyOneNiche;
354    type ContainsIndirections = T::ContainsIndirections;
355    #[cfg(feature = "experimental-ctypes")]
356    type CType = T::CType;
357    const REPORT: &'static report::TypeReport = T::REPORT;
358    const ID: u64 = crate::report::gen_id(Self::REPORT);
359}
360
361/// Used by proc-macros to concatenate fields before wrapping them in a [`Struct`] to compute their layout.
362#[repr(C)]
363#[derive(Default, Clone, Copy)]
364pub struct FieldPair<A, B>(core::marker::PhantomData<(A, B)>);
365/// Used by proc-macros to ensure a list of fields gets the proper end padding.
366#[repr(transparent)]
367pub struct Struct<T>(T);
368
369/// Used by proc-macros to ensure a list of fields gets the proper end padding when specific alignments are requested.
370pub struct AlignedStruct<T, Align>(core::marker::PhantomData<(T, Align)>);
371
372/// Used by [`crate::result::Result`]
373#[repr(C)]
374pub union Union<A, B> {
375    /// The `ok` variant of the union.
376    pub ok: core::mem::ManuallyDrop<A>,
377    /// The `err` variant of the union.
378    pub err: core::mem::ManuallyDrop<B>,
379}
380impl<A, B> Clone for Union<A, B> {
381    fn clone(&self) -> Self {
382        // SAFETY: `Union` is actually `Copy`
383        unsafe { core::ptr::read(self) }
384    }
385}
386
387/// How `stabby` exposes symbols that must be checked through canaries or reflection before being accessed to prevent UB after linking ABI-incompatible functions.
388pub mod checked_import;
389/// ABI-stable compact sum types!
390pub mod enums;
391/// Like [`core::result::Result`], but ABI-stable with niche optimizations!
392pub mod result;
393pub use result::Result;
394/// Like [`core::option::Option`], but ABI-stable with niche optimizations!
395pub mod option;
396pub use option::Option;
397/// A very simple ABI-stable reflection framework.
398pub mod report;
399/// ABI-stable slices.
400pub mod slice;
401/// ABI-stable strs.
402pub mod str;
403/// ABI-stable tuples.
404pub mod tuple {
405    include!(concat!(env!("OUT_DIR"), "/tuples.rs"));
406}
407
408pub use istable::{Array, End, IStable};
409
410/// The heart of `stabby`: the [`IStable`] trait.
411pub mod istable;
412
413/// Expands to [`unreachable!()`](core::unreachable) in debug builds or if `--cfg stabby_check_unreachable=true` has been set in the `RUST_FLAGS`, and to [`core::hint::unreachable_unchecked`] otherwise.
414///
415/// This lets the compiler take advantage of the fact that the code is unreachable in release builds, and optimize accordingly, while giving you the opportunity to double check this at runtime in case of doubts.
416///
417/// # Panics
418/// This macro panics if the code is actually reachable in debug mode.
419/// This would mean that release code would be UB!
420///
421/// # Safety
422/// This macro is inherently unsafe, as it can cause UB in release mode if the code is actually reachable.
423#[macro_export]
424macro_rules! unreachable_unchecked {
425    () => {
426        if cfg!(any(debug_assertions, stabby_check_unreachable = "true")) {
427            ::core::unreachable!()
428        } else {
429            ::core::hint::unreachable_unchecked()
430        }
431    };
432}
433
434/// Expands to [`assert!(condition)`](core::assert) in debug builds or if `--cfg stabby_check_unreachable=true` has been set in the `RUST_FLAGS`, and to [`if condition {core::hint::unreachable_unchecked()}`](core::hint::unreachable_unchecked) otherwise.
435///
436/// This lets the compiler take advantage of the fact that the condition is always true in release builds, and optimize accordingly, while giving you the opportunity to double check this at runtime in case of doubts.
437///
438/// # Panics
439/// This macro panics if the code is actually false in debug mode.
440/// This would mean that release code would be UB!
441///
442/// # Safety
443/// This macro is inherently unsafe, as it can cause UB in release mode if the assertion can actually be false.
444#[macro_export]
445macro_rules! assert_unchecked {
446    ($e: expr, $($t: tt)*) => {
447        if cfg!(any(debug_assertions, stabby_check_unreachable = "true")) {
448            ::core::assert!($e, $($t)*);
449        } else {
450            if !$e {
451                ::core::hint::unreachable_unchecked();
452            }
453        }
454    };
455}
456
457/// Expands to [`assert_eq`](core::assert_eq) in debug builds or if `--cfg stabby_check_unreachable=true` has been set in the `RUST_FLAGS`, and to [`if a != b {core::hint::unreachable_unchecked()}`](core::hint::unreachable_unchecked) otherwise.
458///
459/// This lets the compiler take advantage of the fact that the condition is always true in release builds, and optimize accordingly, while giving you the opportunity to double check this at runtime in case of doubts.
460///
461/// # Panics
462/// This macro panics if the code is actually false in debug mode.
463/// This would mean that release code would be UB!
464///
465/// # Safety
466/// This macro is inherently unsafe, as it can cause UB in release mode if the assertion can actually be false.
467#[macro_export]
468macro_rules! assert_eq_unchecked {
469    ($a: expr, $b: expr, $($t: tt)*) => {
470        if cfg!(any(debug_assertions, stabby_check_unreachable = "true")) {
471            ::core::assert_eq!($a, $b, $($t)*);
472        } else {
473            if $a != $b {
474                ::core::hint::unreachable_unchecked();
475            }
476        }
477    };
478}