stabby_abi/
istable.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::report::TypeReport;
16
17use self::unsigned::{Alignment, IUnsignedBase};
18
19use super::typenum2::*;
20use super::unsigned::{IBitBase, NonZero};
21use super::{AlignedStruct, FieldPair, Struct, Union};
22use stabby_macros::tyeval;
23
24/// A trait to describe the layout of a type, marking it as ABI-stable.
25///
26/// Every layout is assumed to start at the type's first byte.
27///
28/// # Safety
29/// Mis-implementing this trait can lead to memory corruption in sum tyoes
30pub unsafe trait IStable: Sized {
31    /// The size of the annotated type in bytes.
32    type Size: Unsigned;
33    /// The alignment of the annotated type in bytes.
34    type Align: Alignment;
35    /// The values that the annotated type cannot occupy.
36    type ForbiddenValues: IForbiddenValues;
37    /// The padding bits in the annotated types
38    type UnusedBits: IBitMask;
39    /// Allows the detection of whether or not [`core::option::Option`]s are stable:
40    /// - [`B0`] if the type is known to have 0 niches knowable by rustc
41    /// - [`B1`] if the type has exactly one niche value, and that niche is known by rustc
42    /// - [`Saturator`] if the type has more than a single value, which would mean rustc could change its
43    ///   way of representing the [`None`] variant.
44    type HasExactlyOneNiche: ISaturatingAdd;
45    /// Whether or not the type contains indirections (pointers, indices in independent data-structures...)
46    type ContainsIndirections: Bit;
47    #[cfg(feature = "experimental-ctypes")]
48    /// A support mechanism for [`safer-ffi`](https://crates.io/crates/safer-ffi), allowing all [`IStable`] types to also be `safer_ffi::ReprC`
49    type CType: IStable;
50    /// A compile-time generated report of the fields of the type, allowing for compatibility inspection.
51    const REPORT: &'static TypeReport;
52    /// A stable (and ideally unique) identifier for the type. Often generated using [`crate::report::gen_id`], but can be manually set.
53    const ID: u64;
54    /// Returns the size of the type.
55    fn size() -> usize {
56        let size = Self::Size::USIZE;
57        let align = Self::Align::USIZE;
58        size + ((align - (size % align)) % align)
59    }
60    /// Returns the alignment of the type.
61    fn align() -> usize {
62        Self::Align::USIZE
63    }
64    /// Returns `true` if `ptr` points to memory that cannot be a valid value of `Self`.
65    ///
66    /// Note that this function returning `false` is not a guarantee that the value is valid,
67    /// as no heuristic can guarantee that. Notably, this heuristic will generally not look
68    /// through indirections.
69    ///
70    /// # Safety
71    /// Calling this may result in UB if `ptr` points to uninitialized memory at offsets where a forbidden value in `Self` exists.
72    unsafe fn is_invalid(ptr: *const u8) -> bool {
73        Self::ForbiddenValues::is_invalid(ptr)
74    }
75}
76
77/// A static proof that a type is "Plain Old Data".
78///
79/// A type is POD iff copying its byte-representation is sufficient to fully transferring it to
80/// a recipient that shares no other context with the sender. Conditions for this to be true include,
81/// but might not be limited to:
82/// - The type doesn't contain pointers, as they may not point to the same memory on the recipient's end.
83/// - The type doesn't have a destructor, as destructors generally imply a context needs to be cleaned up,
84///   implying that a context exists.
85///
86/// In some circumstances, a POD type may be used as a key in a context (index in an array, key in a HashMap...) that
87/// may not be available to all potential recipient. In such a case, you can wrap that type in [`NotPod`] to strip it
88/// of its POD-ness.
89///
90/// # Safety
91/// Mis-implementing this trait can lead to undefined behaviour, as systems requiring an `IPod` will
92/// assume that `core::ptr::read(slice.as_ptr().cast::<Self>())`, where `slice` is a `&[u8]` that was obtained through
93/// _any_ mean (including reading from a network interface), is _always_ safe provided that the slice was original constructed
94/// by `core::slice::from_raw_parts(&self as *const Self as *const u8, core::mem::size_of::<Self>())`.
95pub unsafe trait IPod: Copy {
96    /// Produces an identifier for the type, allowing to check type at runtime (barring collisions).
97    fn identifier() -> u64;
98}
99// SAFETY: Stabby has proven the type does not contain indirections.
100unsafe impl<T: IStable<ContainsIndirections = B0> + Copy> IPod for T {
101    fn identifier() -> u64 {
102        T::ID
103    }
104}
105
106/// Strips `T` of its status as [Plain Old Data](IPod).
107#[repr(transparent)]
108#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
109pub struct NotPod<T>(pub T);
110impl<T> core::ops::Deref for NotPod<T> {
111    type Target = T;
112    fn deref(&self) -> &Self::Target {
113        &self.0
114    }
115}
116impl<T> core::ops::DerefMut for NotPod<T> {
117    fn deref_mut(&mut self) -> &mut Self::Target {
118        &mut self.0
119    }
120}
121// SAFETY: Simply hides the fact that `T` might not actually contain indirections.
122unsafe impl<T: IStable> IStable for NotPod<T> {
123    type Size = T::Size;
124    type Align = T::Align;
125    type ContainsIndirections = B1;
126    type ForbiddenValues = T::ForbiddenValues;
127    type HasExactlyOneNiche = T::HasExactlyOneNiche;
128    type UnusedBits = T::UnusedBits;
129    #[cfg(feature = "experimental-ctypes")]
130    type CType = T::CType;
131    primitive_report!("NotPod", T);
132}
133
134/// DO NOT PUT THIS IN YOUR OWN STRUCTURE! NOT EVER!!!
135/// IF UNSAFE STRUCTS WERE A THING, THIS WOULD BE IT!!
136///
137/// This structure is used by `#[repr(stabby)]` enums to re-export their niches.
138/// You could theoretically use this to export niches from your own internally tagged unions,
139/// but this is the ONLY pertinent use-case for this struct, and failing to do so properly WILL
140/// make your sum types containing this memory-corruptors.
141#[repr(transparent)]
142pub struct NicheExporter<
143    ForbiddenValues: IForbiddenValues,
144    UnusedBits: IBitMask,
145    HasExactlyOneNiche: ISaturatingAdd,
146>(core::marker::PhantomData<(ForbiddenValues, UnusedBits, HasExactlyOneNiche)>);
147
148impl<
149        ForbiddenValues: IForbiddenValues,
150        UnusedBits: IBitMask,
151        HasExactlyOneNiche: ISaturatingAdd,
152    > Unpin for NicheExporter<ForbiddenValues, UnusedBits, HasExactlyOneNiche>
153{
154}
155
156impl<
157        ForbiddenValues: IForbiddenValues,
158        UnusedBits: IBitMask,
159        HasExactlyOneNiche: ISaturatingAdd,
160    > Clone for NicheExporter<ForbiddenValues, UnusedBits, HasExactlyOneNiche>
161{
162    fn clone(&self) -> Self {
163        *self
164    }
165}
166impl<
167        ForbiddenValues: IForbiddenValues,
168        UnusedBits: IBitMask,
169        HasExactlyOneNiche: ISaturatingAdd,
170    > Copy for NicheExporter<ForbiddenValues, UnusedBits, HasExactlyOneNiche>
171{
172}
173impl<
174        ForbiddenValues: IForbiddenValues,
175        UnusedBits: IBitMask,
176        HasExactlyOneNiche: ISaturatingAdd,
177    > Default for NicheExporter<ForbiddenValues, UnusedBits, HasExactlyOneNiche>
178{
179    fn default() -> Self {
180        Self(core::marker::PhantomData)
181    }
182}
183// SAFETY: This is purely computational
184unsafe impl<
185        ForbiddenValues: IForbiddenValues,
186        UnusedBits: IBitMask,
187        HasExactlyOneNiche: ISaturatingAdd,
188    > IStable for NicheExporter<ForbiddenValues, UnusedBits, HasExactlyOneNiche>
189{
190    type Size = U0;
191    type Align = U1;
192    type ForbiddenValues = ForbiddenValues;
193    type UnusedBits = UnusedBits;
194    type HasExactlyOneNiche = HasExactlyOneNiche;
195    type ContainsIndirections = B0;
196    #[cfg(feature = "experimental-ctypes")]
197    type CType = ();
198    primitive_report!("NicheExporter");
199}
200
201/// The terminator for type-fu lists.
202#[crate::stabby]
203#[derive(Default, Debug, Clone, Copy)]
204pub struct End;
205/// A type-fu linked list.
206pub struct Array<Offset: Unsigned, T, Rest>(core::marker::PhantomData<(Offset, T, Rest)>);
207impl<Offset: Unsigned, T, Rest> Default for Array<Offset, T, Rest> {
208    fn default() -> Self {
209        Self(Default::default())
210    }
211}
212
213/// A multi-byte bitmask.
214pub trait IBitMask {
215    /// Expose the bitmask at runtime.
216    const TUPLE: Self::Tuple;
217    /// The type of the runtime-exposed mask.
218    type Tuple: core::fmt::Debug;
219    /// `Self[O]`
220    type ByteAt<O: Unsigned>: Unsigned;
221    /// `Self | T`
222    type BitOr<T: IBitMask>: IBitMask;
223    /// Shift the bitmask by `O` bytes.
224    type Shift<O: Unsigned>: IBitMask;
225    /// `Self & T`
226    type BitAnd<T: IBitMask>: IBitMask;
227    /// Checks whether the mask is `FF` at index `O`
228    type HasFreeByteAt<O: Unsigned>: Bit;
229    /// Remove the next bit that will be used as a determinant in enums.
230    type ExtractBit: IBitMask;
231    /// Obtain the determinant's offset in bytes.
232    type ExtractedBitByteOffset: Unsigned;
233    /// Obtain the determinant's mask.
234    type ExtractedBitMask: Unsigned;
235}
236impl IBitMask for End {
237    const TUPLE: Self::Tuple = ();
238    type Tuple = ();
239    type ByteAt<O: Unsigned> = U0;
240    type BitOr<T: IBitMask> = T;
241    type Shift<O: Unsigned> = End;
242    type BitAnd<T: IBitMask> = End;
243    type HasFreeByteAt<O: Unsigned> = B0;
244    type ExtractBit = End;
245    type ExtractedBitMask = Saturator;
246    type ExtractedBitByteOffset = Saturator;
247}
248impl<Offset: Unsigned, T: NonZero, Rest: IBitMask> IBitMask for Array<Offset, T, Rest> {
249    const TUPLE: Self::Tuple = ((Offset::USIZE, T::USIZE), Rest::TUPLE);
250    type Tuple = ((usize, usize), Rest::Tuple);
251    type ByteAt<O: Unsigned> = <Offset::Equal<O> as Bit>::UTernary<T, Rest::ByteAt<O>>;
252    type BitAnd<Mask: IBitMask> =
253        <<T::BitAnd<Mask::ByteAt<Offset>> as Unsigned>::Equal<U0> as Bit>::BmTernary<
254            Rest::BitAnd<Mask>,
255            Array<
256                Offset,
257                <T::BitAnd<Mask::ByteAt<Offset>> as Unsigned>::NonZero,
258                Rest::BitAnd<Mask>,
259            >,
260        >;
261    type BitOr<Arr: IBitMask> = Array<Offset, T, Rest::BitOr<Arr>>;
262    type Shift<O: Unsigned> = Array<Offset::Add<O>, T, Rest::Shift<O>>;
263    type HasFreeByteAt<O: Unsigned> =
264        <<O::Equal<Offset> as Bit>::And<T::Equal<UxFF>> as Bit>::Or<Rest::HasFreeByteAt<O>>;
265    type ExtractBit =
266        <<T::AbsSub<T::TruncateAtRightmostOne> as Unsigned>::Greater<U0> as Bit>::BmTernary<
267            Array<Offset, <T::AbsSub<T::TruncateAtRightmostOne> as Unsigned>::NonZero, Rest>,
268            Rest,
269        >;
270    type ExtractedBitByteOffset = Offset;
271    type ExtractedBitMask = T::TruncateAtRightmostOne;
272}
273/// A set of possibly multi-byte forbidden values.
274pub trait IForbiddenValues {
275    /// Shift all values in the set by `O` bytes
276    type Shift<O: Unsigned>: IForbiddenValues;
277    /// `union(Self, T)`
278    type Or<T: IForbiddenValues>: IForbiddenValues;
279    /// Extract a single forbidden value that fits within `Mask`
280    type SelectFrom<Mask: IBitMask>: ISingleForbiddenValue;
281    /// Extract the first available forbidden value.
282    type SelectOne: ISingleForbiddenValue;
283    /// Returns `true` if `ptr` points to a forbidden value.
284    ///
285    /// # Safety
286    /// Calling this on uninitialized memory is UB.
287    unsafe fn is_invalid(ptr: *const u8) -> bool;
288}
289/// A single multi-byte forbidden value.
290pub trait ISingleForbiddenValue {
291    /// Add a byte to the forbidden value.
292    type Push<O: Unsigned, T>: ISingleForbiddenValue;
293    /// `Self == End ? T : Self`
294    type Or<T: ISingleForbiddenValue>: ISingleForbiddenValue;
295    /// `T == End ? Self : T`
296    type And<T: ISingleForbiddenValue>: ISingleForbiddenValue;
297    /// Turns Saturators into End.
298    type Resolve: ISingleForbiddenValue;
299}
300impl IForbiddenValues for End {
301    type Shift<O: Unsigned> = End;
302    type Or<T: IForbiddenValues> = T;
303    type SelectFrom<Mask: IBitMask> = End;
304    type SelectOne = End;
305    unsafe fn is_invalid(_: *const u8) -> bool {
306        false
307    }
308}
309impl ISingleForbiddenValue for Saturator {
310    type Push<O: Unsigned, T> = Saturator;
311    type Or<T: ISingleForbiddenValue> = T;
312    type And<T: ISingleForbiddenValue> = Saturator;
313    type Resolve = End;
314}
315impl ISingleForbiddenValue for End {
316    type Push<O: Unsigned, T> = Array<O, T, Self>;
317    type Or<T: ISingleForbiddenValue> = T;
318    type And<T: ISingleForbiddenValue> = T;
319    type Resolve = Self;
320}
321impl<Offset: Unsigned, T, Rest: ISingleForbiddenValue> ISingleForbiddenValue
322    for Array<Offset, T, Rest>
323{
324    type Push<O: Unsigned, V> = Array<O, V, Self>;
325    type Or<V: ISingleForbiddenValue> = Self;
326    type And<V: ISingleForbiddenValue> = V;
327    type Resolve = Self;
328}
329impl<Offset: Unsigned, T: Unsigned, Rest: IForbiddenValues> IForbiddenValues
330    for Array<Offset, T, Rest>
331{
332    type Shift<O: Unsigned> = Array<Offset::Add<O>, T, Rest::Shift<O>>;
333    type Or<O: IForbiddenValues> = Or<O, Self>;
334    type SelectFrom<Mask: IBitMask> =
335        <<Mask::HasFreeByteAt<Offset> as IBitBase>::AsForbiddenValue as ISingleForbiddenValue>::And<
336            <Rest::SelectFrom<Mask> as ISingleForbiddenValue>::Push<Offset, T>,
337        >;
338    type SelectOne = Array<Offset, T, Rest::SelectOne>;
339    unsafe fn is_invalid(ptr: *const u8) -> bool {
340        ptr.add(Offset::USIZE).read() == T::U8 && Rest::is_invalid(ptr)
341    }
342}
343impl<A: IForbiddenValues, B: IForbiddenValues> IForbiddenValues for Or<A, B> {
344    type Shift<O: Unsigned> = Or<A::Shift<O>, B::Shift<O>>;
345    type Or<T: IForbiddenValues> = Or<T, Self>;
346    type SelectFrom<Mask: IBitMask> =
347        <A::SelectFrom<Mask> as ISingleForbiddenValue>::Or<B::SelectFrom<Mask>>;
348    type SelectOne = A::SelectOne;
349    unsafe fn is_invalid(ptr: *const u8) -> bool {
350        A::is_invalid(ptr) || B::is_invalid(ptr)
351    }
352}
353/// An inclusive range of forbidden values for a single byte.
354pub struct ForbiddenRange<Min: Unsigned, Max: Unsigned<Greater<Min> = B1>, Offset: Unsigned>(
355    core::marker::PhantomData<(Min, Max, Offset)>,
356);
357impl<Min: Unsigned, Max: Unsigned<Greater<Min> = B1>, Offset: Unsigned> IForbiddenValues
358    for ForbiddenRange<Min, Max, Offset>
359{
360    type Shift<O: Unsigned> = ForbiddenRange<Min, Max, Offset::Add<O>>;
361    type Or<T: IForbiddenValues> = Or<Self, T>;
362    type SelectFrom<Mask: IBitMask> =
363        <Mask::HasFreeByteAt<Offset> as IBitBase>::_SfvTernary<Self::SelectOne, End>;
364    type SelectOne = Array<Offset, Min, End>;
365    unsafe fn is_invalid(ptr: *const u8) -> bool {
366        let v = ptr.add(Offset::USIZE).read();
367        Min::U8 <= v && v <= Max::U8
368    }
369}
370/// The union of 2 sets.
371pub struct Or<A, B>(core::marker::PhantomData<(A, B)>);
372/// Whether or not the type is the end of a list.
373pub trait IsEnd {
374    /// The result
375    type Output: Bit;
376}
377impl IsEnd for End {
378    type Output = B1;
379}
380impl<O: Unsigned, T, R: IBitMask> IsEnd for Array<O, T, R> {
381    type Output = B0;
382}
383
384// SAFETY: Purely computational. Heavily tested.
385unsafe impl<A: IStable, B: IStable> IStable for FieldPair<A, B> {
386    type ForbiddenValues =
387        Or<A::ForbiddenValues, <AlignedAfter<B, A::Size> as IStable>::ForbiddenValues>;
388    type UnusedBits =
389        <A::UnusedBits as IBitMask>::BitOr<<AlignedAfter<B, A::Size> as IStable>::UnusedBits>;
390    type Size = <AlignedAfter<B, A::Size> as IStable>::Size;
391    type Align = <A::Align as Alignment>::Max<B::Align>;
392    type HasExactlyOneNiche =
393        <<Self::Size as Unsigned>::Equal<<A::Size as Unsigned>::Add<B::Size>> as Bit>::SaddTernary<
394            <A::HasExactlyOneNiche as ISaturatingAdd>::SaturatingAdd<
395                <AlignedAfter<B, A::Size> as IStable>::HasExactlyOneNiche,
396            >,
397            Saturator,
398        >;
399    type ContainsIndirections = <A::ContainsIndirections as Bit>::Or<B::ContainsIndirections>;
400    #[cfg(feature = "experimental-ctypes")]
401    type CType = ();
402    primitive_report!("FP");
403}
404/// Runtime values for [`ISaturatingAdd`]
405pub enum SaturatingAddValue {
406    /// 0
407    B0,
408    /// 1
409    B1,
410    /// More than 1
411    Saturator,
412}
413/// An addition that saturates at 2.
414pub trait ISaturatingAdd {
415    /// Runtime value.
416    const VALUE: SaturatingAddValue;
417    /// sat_add(Self, 1)
418    type SaturatingAddB1: ISaturatingAdd;
419    /// sat_add(Self, B)
420    type SaturatingAdd<B: ISaturatingAdd>: ISaturatingAdd;
421}
422impl ISaturatingAdd for B0 {
423    const VALUE: SaturatingAddValue = SaturatingAddValue::B0;
424    type SaturatingAdd<B: ISaturatingAdd> = B;
425    type SaturatingAddB1 = B1;
426}
427impl ISaturatingAdd for B1 {
428    const VALUE: SaturatingAddValue = SaturatingAddValue::B1;
429    type SaturatingAddB1 = Saturator;
430    type SaturatingAdd<B: ISaturatingAdd> = B::SaturatingAddB1;
431}
432impl ISaturatingAdd for Saturator {
433    const VALUE: SaturatingAddValue = SaturatingAddValue::Saturator;
434    type SaturatingAddB1 = Saturator;
435    type SaturatingAdd<B: ISaturatingAdd> = Saturator;
436}
437#[derive(Default)]
438/// An Exception-like value that indicates a computation can never succeed.
439pub struct Saturator;
440
441/// Whether or not a value is included in a set.
442pub trait Includes<SubSet> {
443    /// The result
444    type Output;
445}
446impl<T> Includes<End> for T {
447    type Output = End;
448}
449impl<O: Unsigned, T, R: IBitMask> Includes<Array<O, T, R>> for End {
450    type Output = End;
451}
452impl<O1: Unsigned, T1, R1: IBitMask, O2: Unsigned, T2, R2: IBitMask> Includes<Array<O1, T1, R1>>
453    for Array<O2, T2, R2>
454where
455    Array<O2, T2, R2>: IncludesComputer<(O1, T1)> + Includes<R1>,
456    R1: IsEnd,
457    <Self as Includes<R1>>::Output: IsEnd,
458    (
459        <Self as IncludesComputer<(O1, T1)>>::Output,
460        <Self as Includes<R1>>::Output,
461        <<Self as Includes<R1>>::Output as IsEnd>::Output,
462        <R1 as IsEnd>::Output,
463    ): Arrayify,
464{
465    type Output = <(
466        <Self as IncludesComputer<(O1, T1)>>::Output,
467        <Self as Includes<R1>>::Output,
468        <<Self as Includes<R1>>::Output as IsEnd>::Output,
469        <R1 as IsEnd>::Output,
470    ) as Arrayify>::Output;
471}
472impl<O1: Unsigned, T1> Arrayify for ((O1, T1), End, B1, B1) {
473    type Output = Array<O1, T1, End>;
474}
475impl<O1: Unsigned, T1> Arrayify for ((O1, T1), End, B1, B0) {
476    type Output = End;
477}
478impl<O1: Unsigned, T1, Tail: IBitMask> Arrayify for ((O1, T1), Tail, B0, B0) {
479    type Output = Array<O1, T1, Tail>;
480}
481impl<Tail, T, U> Arrayify for (End, Tail, T, U) {
482    type Output = End;
483}
484/// Support for stabby computations
485pub trait Arrayify {
486    /// Support for stabby computations
487    type Output;
488}
489/// Support for stabby computations
490pub trait IncludesComputer<SubSet> {
491    /// Support for stabby computations
492    type Output;
493}
494impl<O1: Unsigned, T1, O2: Unsigned, T2, R2: IBitMask> IncludesComputer<(O1, T1)>
495    for Array<O2, T2, R2>
496where
497    Self: IncludesComputer<(O1, T1, tyeval!(O1 == O2))>,
498{
499    type Output = <Self as IncludesComputer<(O1, T1, tyeval!(O1 == O2))>>::Output;
500}
501impl<O1: Unsigned, T1, O2: Unsigned, T2, R2: IBitMask> IncludesComputer<(O1, T1, B0)>
502    for Array<O2, T2, R2>
503where
504    R2: IncludesComputer<(O1, T1)>,
505{
506    type Output = <R2 as IncludesComputer<(O1, T1)>>::Output;
507}
508impl<O1: Unsigned, T1, O2: Unsigned, T2: Unsigned, R2: IBitMask> IncludesComputer<(O1, T1, B1)>
509    for Array<O2, T2, R2>
510where
511    Self: IncludesComputer<(O1, T1, B1, tyeval!(T2 == U255))>,
512{
513    type Output = <Self as IncludesComputer<(O1, T1, B1, tyeval!(T2 == U255))>>::Output;
514}
515impl<O1: Unsigned, T1, O2: Unsigned, T2, R2: IBitMask> IncludesComputer<(O1, T1, B1, B1)>
516    for Array<O2, T2, R2>
517{
518    type Output = (O1, T1);
519}
520impl<O1: Unsigned, T1, O2: Unsigned, T2, R2: IBitMask> IncludesComputer<(O1, T1, B1, B0)>
521    for Array<O2, T2, R2>
522{
523    type Output = End;
524}
525
526// SAFETY: Purely computational. Heavily tested.
527unsafe impl<A: IStable, B: IStable> IStable for Union<A, B> {
528    type ForbiddenValues = End;
529    type UnusedBits = End;
530    type Size = <<A::Size as Unsigned>::Max<B::Size> as Unsigned>::NextMultipleOf<Self::Align>;
531    type Align = <A::Align as Alignment>::Max<B::Align>;
532    type HasExactlyOneNiche = B0;
533    type ContainsIndirections = <A::ContainsIndirections as Bit>::Or<B::ContainsIndirections>;
534    #[cfg(feature = "experimental-ctypes")]
535    type CType = <<Self::Align as PowerOf2>::Divide<Self::Size> as IUnsignedBase>::Array<
536        <Self::Align as Alignment>::AsUint,
537    >;
538    primitive_report!("Union");
539}
540
541/// Computes a `T`-typed field's layout when it's after `Start` bytes, taking `T`'s alignment into account.
542pub struct AlignedAfter<T, Start: Unsigned>(core::marker::PhantomData<(T, Start)>);
543
544// SAFETY: Purely computational. Heavily tested.
545unsafe impl<T: IStable, Start: Unsigned> IStable for AlignedAfter<T, Start> {
546    type Align = T::Align;
547    type Size = <T::Size as Unsigned>::Add<Start::NextMultipleOf<T::Align>>;
548    type ForbiddenValues =
549        <T::ForbiddenValues as IForbiddenValues>::Shift<Start::NextMultipleOf<T::Align>>;
550    type UnusedBits = <<<tyeval!(Start::NextMultipleOf<T::Align> - Start) as IUnsignedBase>::PaddingBitMask as IBitMask>::Shift<Start> as IBitMask>::BitOr<
551        <T::UnusedBits as IBitMask>::Shift<Start::NextMultipleOf<T::Align>>,
552    >;
553    type HasExactlyOneNiche = T::HasExactlyOneNiche;
554    type ContainsIndirections = T::ContainsIndirections;
555    #[cfg(feature = "experimental-ctypes")]
556    type CType = ();
557    primitive_report!("FP");
558}
559
560// SAFETY: Purely computational. Heavily tested.
561unsafe impl<T: IStable> IStable for Struct<T> {
562    type Size = <T::Size as Unsigned>::NextMultipleOf<T::Align>;
563    type Align = T::Align;
564    type ForbiddenValues = T::ForbiddenValues;
565    type UnusedBits = <T::UnusedBits as IBitMask>::BitOr<
566        <<tyeval!(<T::Size as Unsigned>::NextMultipleOf<T::Align> - T::Size) as IUnsignedBase>::PaddingBitMask as IBitMask>::Shift<T::Size>>;
567    type HasExactlyOneNiche = <<T::Size as Unsigned>::Equal<Self::Size> as Bit>::SaddTernary<
568        T::HasExactlyOneNiche,
569        Saturator,
570    >;
571    type ContainsIndirections = T::ContainsIndirections;
572    #[cfg(feature = "experimental-ctypes")]
573    type CType = ();
574    primitive_report!("FP");
575}
576
577// SAFETY: Purely computational. Heavily tested.
578unsafe impl<T: IStable, Align: Alignment> IStable for AlignedStruct<T, Align> {
579    type Size = <T::Size as Unsigned>::NextMultipleOf<Self::Align>;
580    type Align = <Align as Alignment>::Max<T::Align>;
581    type ForbiddenValues = T::ForbiddenValues;
582    type UnusedBits = <T::UnusedBits as IBitMask>::BitOr<
583        <<tyeval!(<T::Size as Unsigned>::NextMultipleOf<T::Align> - T::Size) as IUnsignedBase>::PaddingBitMask as IBitMask>::Shift<T::Size>>;
584    type HasExactlyOneNiche = <<T::Size as Unsigned>::Equal<Self::Size> as Bit>::SaddTernary<
585        T::HasExactlyOneNiche,
586        Saturator,
587    >;
588    type ContainsIndirections = T::ContainsIndirections;
589    #[cfg(feature = "experimental-ctypes")]
590    type CType = ();
591    primitive_report!("FP");
592}
593
594/// Used by `stabby` to prevent proof cycles in types that contain indirections to themselves.
595#[crate::stabby]
596pub struct _Self;