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}