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    #[rustversion::attr(since(1.86), const)]
244    pub unsafe fn as_mut_unchecked(&mut self) -> &mut T {
245        &mut self.value
246    }
247    /// # Safety
248    /// This is only safe if `T` is FFI-safe, or if this `self` was constructed from a value
249    /// of `T` that was instanciated within the same shared object.
250    pub unsafe fn into_inner_unchecked(self) -> T {
251        self.value
252    }
253    /// Extracts the inner value from `self`
254    pub fn into_inner(self) -> T
255    where
256        T: IStable,
257    {
258        self.value
259    }
260}
261
262unsafe impl<T, As: IStable> IStable for StableLike<T, As> {
263    type Size = As::Size;
264    type Align = As::Align;
265    type ForbiddenValues = As::ForbiddenValues;
266    type UnusedBits = As::UnusedBits;
267    type HasExactlyOneNiche = As::HasExactlyOneNiche;
268    type ContainsIndirections = As::ContainsIndirections;
269    #[cfg(feature = "experimental-ctypes")]
270    type CType = As::CType;
271    const ID: u64 = crate::report::gen_id(Self::REPORT);
272    const REPORT: &'static report::TypeReport = As::REPORT;
273}
274
275/// Emulates a type of size `Size` and alignment `Align`.
276///
277/// Note that this is not a ZST, and that you may pass [`B0`] or [`B1`] as the this generic parameter if you
278/// want to inform `stabby` that the type it emulates has exactly zero or one niche respectively that the
279/// compiler knows about. This information can be used by `stabby` to determine that `core::option::Option`s
280/// transitively containing the emulated type are indeed ABI-stable.
281pub struct NoNiches<
282    Size: Unsigned,
283    Align: Alignment,
284    HasExactlyOneNiche: ISaturatingAdd = Saturator,
285    ContainsIndirections: Bit = B0,
286>(
287    Size::Padding,
288    core::marker::PhantomData<(Size, Align, HasExactlyOneNiche, ContainsIndirections)>,
289);
290unsafe impl<
291        Size: Unsigned,
292        Align: Alignment,
293        HasExactlyOneNiche: ISaturatingAdd,
294        ContainsIndirections: Bit,
295    > IStable for NoNiches<Size, Align, HasExactlyOneNiche, ContainsIndirections>
296{
297    type Size = Size;
298    type Align = Align;
299    type ForbiddenValues = End;
300    type UnusedBits = End;
301    type HasExactlyOneNiche = HasExactlyOneNiche;
302    type ContainsIndirections = ContainsIndirections;
303    #[cfg(feature = "experimental-ctypes")]
304    type CType = ();
305    primitive_report!("NoNiches");
306}
307
308/// Allows removing the [`IStable`] implementation from `T` if `Cond` is not also ABI-stable.
309///
310/// This is typically used in combination with [`StableLike`], for example in vtables to mark function
311/// pointers as stable only if all of their arguments are stable.
312#[repr(C)]
313pub struct StableIf<T, Cond> {
314    /// The actual value
315    pub value: T,
316    marker: core::marker::PhantomData<Cond>,
317}
318impl<T: Clone, Cond> Clone for StableIf<T, Cond> {
319    fn clone(&self) -> Self {
320        Self {
321            value: self.value.clone(),
322            marker: self.marker,
323        }
324    }
325}
326impl<T: Copy, Cond> Copy for StableIf<T, Cond> {}
327impl<T, Cond> StableIf<T, Cond> {
328    /// # Safety
329    /// Refer to type documentation
330    pub const unsafe fn new(value: T) -> Self {
331        Self {
332            value,
333            marker: core::marker::PhantomData,
334        }
335    }
336}
337
338impl<T, Cond> core::ops::Deref for StableIf<T, Cond> {
339    type Target = T;
340    fn deref(&self) -> &Self::Target {
341        &self.value
342    }
343}
344impl<T, Cond> core::ops::DerefMut for StableIf<T, Cond> {
345    fn deref_mut(&mut self) -> &mut Self::Target {
346        &mut self.value
347    }
348}
349unsafe impl<T: IStable, Cond: IStable> IStable for StableIf<T, Cond> {
350    type Size = T::Size;
351    type Align = T::Align;
352    type ForbiddenValues = T::ForbiddenValues;
353    type UnusedBits = T::UnusedBits;
354    type HasExactlyOneNiche = T::HasExactlyOneNiche;
355    type ContainsIndirections = T::ContainsIndirections;
356    #[cfg(feature = "experimental-ctypes")]
357    type CType = T::CType;
358    const REPORT: &'static report::TypeReport = T::REPORT;
359    const ID: u64 = crate::report::gen_id(Self::REPORT);
360}
361
362/// Used by proc-macros to concatenate fields before wrapping them in a [`Struct`] to compute their layout.
363#[repr(C)]
364#[derive(Default, Clone, Copy)]
365pub struct FieldPair<A, B>(core::marker::PhantomData<(A, B)>);
366/// Used by proc-macros to ensure a list of fields gets the proper end padding.
367#[repr(transparent)]
368pub struct Struct<T>(T);
369
370/// Used by proc-macros to ensure a list of fields gets the proper end padding when specific alignments are requested.
371pub struct AlignedStruct<T, Align>(core::marker::PhantomData<(T, Align)>);
372
373/// Used by [`crate::result::Result`]
374#[repr(C)]
375pub union Union<A, B> {
376    /// The `ok` variant of the union.
377    pub ok: core::mem::ManuallyDrop<A>,
378    /// The `err` variant of the union.
379    pub err: core::mem::ManuallyDrop<B>,
380}
381impl<A, B> Clone for Union<A, B> {
382    fn clone(&self) -> Self {
383        // SAFETY: `Union` is actually `Copy`
384        unsafe { core::ptr::read(self) }
385    }
386}
387
388/// How `stabby` exposes symbols that must be checked through canaries or reflection before being accessed to prevent UB after linking ABI-incompatible functions.
389pub mod checked_import;
390/// ABI-stable compact sum types!
391pub mod enums;
392/// Like [`core::result::Result`], but ABI-stable with niche optimizations!
393pub mod result;
394pub use result::Result;
395/// Like [`core::option::Option`], but ABI-stable with niche optimizations!
396pub mod option;
397pub use option::Option;
398/// A very simple ABI-stable reflection framework.
399pub mod report;
400/// ABI-stable slices.
401pub mod slice;
402/// ABI-stable strs.
403pub mod str;
404/// ABI-stable tuples.
405pub mod tuple {
406    include!(concat!(env!("OUT_DIR"), "/tuples.rs"));
407}
408
409pub use istable::{Array, End, IStable};
410
411/// The heart of `stabby`: the [`IStable`] trait.
412pub mod istable;
413
414/// 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.
415///
416/// 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.
417///
418/// # Panics
419/// This macro panics if the code is actually reachable in debug mode.
420/// This would mean that release code would be UB!
421///
422/// # Safety
423/// This macro is inherently unsafe, as it can cause UB in release mode if the code is actually reachable.
424#[macro_export]
425macro_rules! unreachable_unchecked {
426    () => {
427        if cfg!(any(debug_assertions, stabby_check_unreachable = "true")) {
428            ::core::unreachable!()
429        } else {
430            ::core::hint::unreachable_unchecked()
431        }
432    };
433}
434
435/// 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.
436///
437/// 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.
438///
439/// # Panics
440/// This macro panics if the code is actually false in debug mode.
441/// This would mean that release code would be UB!
442///
443/// # Safety
444/// This macro is inherently unsafe, as it can cause UB in release mode if the assertion can actually be false.
445#[macro_export]
446macro_rules! assert_unchecked {
447    ($e: expr, $($t: tt)*) => {
448        if cfg!(any(debug_assertions, stabby_check_unreachable = "true")) {
449            ::core::assert!($e, $($t)*);
450        } else {
451            if !$e {
452                ::core::hint::unreachable_unchecked();
453            }
454        }
455    };
456}
457
458/// 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.
459///
460/// 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.
461///
462/// # Panics
463/// This macro panics if the code is actually false in debug mode.
464/// This would mean that release code would be UB!
465///
466/// # Safety
467/// This macro is inherently unsafe, as it can cause UB in release mode if the assertion can actually be false.
468#[macro_export]
469macro_rules! assert_eq_unchecked {
470    ($a: expr, $b: expr, $($t: tt)*) => {
471        if cfg!(any(debug_assertions, stabby_check_unreachable = "true")) {
472            ::core::assert_eq!($a, $b, $($t)*);
473        } else {
474            if $a != $b {
475                ::core::hint::unreachable_unchecked();
476            }
477        }
478    };
479}