stabby_abi/enums/
mod.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::marker::PhantomData;
16use stabby_macros::tyeval;
17
18use super::{
19    istable::{IBitMask, IForbiddenValues, ISingleForbiddenValue, NicheExporter, Saturator},
20    unsigned::NonZero,
21    vtable::{H, T},
22    IStable,
23};
24use crate::*;
25
26/// A type that can inspect a union to detect if it's in `ok` or `err` state.
27pub trait IDeterminant: IStable {
28    /// Sets the union in `ok` state.
29    /// # Safety
30    /// This function MUST be called after setting `union` to a valid value for type `Ok`
31    unsafe fn ok(union: *mut u8) -> Self;
32    /// Sets the union in `err` state.
33    /// # Safety
34    /// This function MUST be called after setting `union` to a valid value for type `Err`
35    unsafe fn err(union: *mut u8) -> Self;
36    /// Returns the state of the union.
37    fn is_det_ok(&self, union: *const u8) -> bool;
38    /// Whether the determinant is explicit or implicit.
39    type IsNicheTrick: Bit;
40}
41
42/// If no niche can be found, an external tag is used.
43#[repr(u8)]
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub enum BitDeterminant {
46    /// The union is in the `ok` state.
47    Ok = 0,
48    /// The union is in the `err` state.
49    Err = 1,
50}
51// SAFETY: This is core to stabby's sum types: any sum type is a test of this.
52unsafe impl IStable for BitDeterminant {
53    type Size = U1;
54    type Align = U1;
55    type ForbiddenValues = End;
56    type UnusedBits = Array<U0, U254, End>;
57    type HasExactlyOneNiche = Saturator;
58    type ContainsIndirections = B0;
59    #[cfg(feature = "experimental-ctypes")]
60    type CType = u8;
61    primitive_report!("BitDeterminant");
62}
63
64impl IDeterminant for BitDeterminant {
65    unsafe fn ok(_: *mut u8) -> Self {
66        BitDeterminant::Ok
67    }
68    unsafe fn err(_: *mut u8) -> Self {
69        BitDeterminant::Err
70    }
71    fn is_det_ok(&self, _: *const u8) -> bool {
72        (*self as u8 & 1) == 0
73    }
74    type IsNicheTrick = B0;
75}
76impl IDeterminant for End {
77    unsafe fn ok(_: *mut u8) -> Self {
78        End
79    }
80    unsafe fn err(_: *mut u8) -> Self {
81        End
82    }
83    fn is_det_ok(&self, _: *const u8) -> bool {
84        false
85    }
86    type IsNicheTrick = B0;
87}
88
89/// Indicates that if the `Offset`th byte equals `Value`, and that the `Tail` also says so, `Err` is the current variant of the inspected union.
90#[derive(Clone, Copy)]
91#[repr(C)]
92pub struct ValueIsErr<Offset, Value, Tail: IStable>(PhantomData<(Offset, Value)>, Tail);
93impl<Offset, Value, Tail: IStable> Unpin for ValueIsErr<Offset, Value, Tail> {}
94// SAFETY: This is core to stabby's sum types: any sum type is a test of this.
95unsafe impl<Offset, Value, Tail: IStable> IStable for ValueIsErr<Offset, Value, Tail> {
96    type Size = Tail::Size;
97    type Align = Tail::Align;
98    type ForbiddenValues = Tail::ForbiddenValues;
99    type UnusedBits = Tail::UnusedBits;
100    type HasExactlyOneNiche = Tail::HasExactlyOneNiche;
101    type ContainsIndirections = B0;
102    #[cfg(feature = "experimental-ctypes")]
103    type CType = ();
104    primitive_report!("ValueIsErr");
105}
106impl<Offset: Unsigned, Value: Unsigned, Tail: IDeterminant + core::fmt::Debug> core::fmt::Debug
107    for ValueIsErr<Offset, Value, Tail>
108{
109    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110        write!(
111            f,
112            "ValIsErr(ptr[{}]={}, {:?})",
113            Offset::USIZE,
114            Value::U8,
115            &self.1,
116        )
117    }
118}
119impl<Offset: Unsigned, Value: Unsigned, Tail: IDeterminant> IDeterminant
120    for ValueIsErr<Offset, Value, Tail>
121where
122    ValueIsErr<Offset, Value, Tail>: IStable,
123{
124    unsafe fn ok(union: *mut u8) -> Self {
125        ValueIsErr(PhantomData, Tail::ok(union))
126    }
127    unsafe fn err(union: *mut u8) -> Self {
128        let ptr = union;
129        *ptr.add(Offset::USIZE) = Value::U8;
130        ValueIsErr(PhantomData, Tail::err(union))
131    }
132    fn is_det_ok(&self, union: *const u8) -> bool {
133        let ptr = union;
134        unsafe { *ptr.add(Offset::USIZE) != Value::U8 || self.1.is_det_ok(union) }
135    }
136    type IsNicheTrick = B1;
137}
138/// Coerces a type into a [`ValueIsErr`].
139pub trait IntoValueIsErr {
140    /// The coerced type.
141    type ValueIsErr: IDeterminant + IStable + Unpin;
142}
143impl IntoValueIsErr for End {
144    type ValueIsErr = End;
145}
146impl<Offset: Unsigned, Value: Unsigned, Tail: IForbiddenValues + IntoValueIsErr> IntoValueIsErr
147    for Array<Offset, Value, Tail>
148{
149    type ValueIsErr = ValueIsErr<Offset, Value, Tail::ValueIsErr>;
150}
151/// Indicates that if the `Offset`th byte bitanded with `Mask` is non-zero, `Err` is the current variant of the inspected union.
152#[crate::stabby]
153#[derive(Clone, Copy)]
154pub struct BitIsErr<Offset, Mask>(PhantomData<(Offset, Mask)>);
155impl<Offset: Unsigned, Mask: Unsigned> core::fmt::Debug for BitIsErr<Offset, Mask> {
156    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157        write!(f, "BitIsErr(ptr[{}] & {})", Offset::USIZE, Mask::U8)
158    }
159}
160impl<Offset, Mask> Unpin for BitIsErr<Offset, Mask> {}
161impl<Offset: Unsigned, Mask: Unsigned> IDeterminant for BitIsErr<Offset, Mask> {
162    unsafe fn ok(union: *mut u8) -> Self {
163        let ptr = union;
164        if Mask::U8 == 1 {
165            *ptr.add(Offset::USIZE) = 0;
166        }
167        *ptr.add(Offset::USIZE) &= u8::MAX ^ Mask::U8;
168        BitIsErr(PhantomData)
169    }
170    unsafe fn err(union: *mut u8) -> Self {
171        let ptr = union;
172        if Mask::U8 == 1 {
173            *ptr.add(Offset::USIZE) = 0;
174        }
175        *ptr.add(Offset::USIZE) |= Mask::U8;
176        BitIsErr(PhantomData)
177    }
178    fn is_det_ok(&self, union: *const u8) -> bool {
179        let ptr = union;
180        unsafe { *ptr.add(Offset::USIZE) & Mask::U8 == 0 }
181    }
182    type IsNicheTrick = B1;
183}
184/// Inverts the return value of `Determinant`'s inspection.
185#[derive(Debug, Clone, Copy)]
186pub struct Not<Determinant>(Determinant);
187impl<Determinant> Unpin for Not<Determinant> {}
188// SAFETY: This is core to stabby's sum types: any sum type is a test of this.
189unsafe impl<Determinant: IStable> IStable for Not<Determinant> {
190    type Size = Determinant::Size;
191    type Align = Determinant::Align;
192    type ForbiddenValues = Determinant::ForbiddenValues;
193    type UnusedBits = Determinant::UnusedBits;
194    type HasExactlyOneNiche = Determinant::HasExactlyOneNiche;
195    type ContainsIndirections = Determinant::ContainsIndirections;
196    #[cfg(feature = "experimental-ctypes")]
197    type CType = Determinant::CType;
198    primitive_report!("Not", Determinant);
199}
200impl<Determinant: IDeterminant> IDeterminant for Not<Determinant>
201where
202    Not<Determinant>: IStable,
203{
204    unsafe fn ok(union: *mut u8) -> Self {
205        Not(Determinant::err(union))
206    }
207    unsafe fn err(union: *mut u8) -> Self {
208        Not(Determinant::ok(union))
209    }
210    fn is_det_ok(&self, union: *const u8) -> bool {
211        !self.0.is_det_ok(union)
212    }
213    type IsNicheTrick = Determinant::IsNicheTrick;
214}
215
216// "And now for the tricky bit..."
217///Proof that stabby can construct a [`crate::Result`] based on `Self` and `Other`'s niches.
218pub trait IDeterminantProvider<Other>: IStable {
219    /// How much the `Ok` variant must be shifted.
220    type OkShift: Unsigned;
221    /// How much the `Err` variant must be shifted.
222    type ErrShift: Unsigned;
223    /// The discriminant.
224    type Determinant: IDeterminant + Unpin;
225    /// The remaining niches.
226    type NicheExporter: IStable + Default + Copy + Unpin;
227}
228mod seal {
229    use super::*;
230    pub trait IDeterminantProviderInnerRev {
231        type OkShift: Unsigned;
232        type ErrShift: Unsigned;
233        type Determinant: IDeterminant + Unpin;
234        type NicheExporter: IStable + Default + Copy + Unpin;
235    }
236    pub trait IDeterminantProviderInner {
237        type ErrShift: Unsigned;
238        type Determinant: IDeterminant + Unpin;
239        type NicheExporter: IStable + Default + Copy + Unpin;
240    }
241}
242pub(crate) use seal::*;
243
244/// The alignment of `Union<Ok, Err>`
245type UnionAlign<Ok, Err> = <<Ok as IStable>::Align as PowerOf2>::Max<<Err as IStable>::Align>;
246/// The size of `Union<Ok, Err>`
247type UnionSize<Ok, Err, OkShift, ErrShift> =
248    <<tyeval!(<Ok as IStable>::Size + OkShift) as Unsigned>::Max<
249        tyeval!(<Err as IStable>::Size + ErrShift),
250    > as Unsigned>::NextMultipleOf<UnionAlign<Ok, Err>>;
251/// T::Size + Shift
252type PaddedSize<T, Shift> = <<T as IStable>::Size as Unsigned>::Add<Shift>;
253/// T's unused bits, shifted by Shift bytes
254type ShiftedUnusedBits<T, Shift> = <<T as IStable>::UnusedBits as IBitMask>::Shift<Shift>;
255
256/// The unused bits of the Ok variant in a Ok-Err union where the Ok is placed OkShift bytes from the left
257pub(crate) type UnionMemberUnusedBits<Ok, Err, OkShift> =
258    <<<<OkShift as Unsigned>::Padding as IStable>::UnusedBits as IBitMask>::BitOr<
259        ShiftedUnusedBits<Ok, OkShift>,
260    > as IBitMask>::BitOr<
261        ShiftedUnusedBits<
262            <tyeval!(UnionSize<Ok, Err, OkShift, U0> - PaddedSize<Ok, OkShift>) as Unsigned>::Padding,
263            PaddedSize<Ok, OkShift>,
264        >,
265    >;
266
267macro_rules! same_as {
268    ($T: ty) => {
269        type ErrShift = <$T as IDeterminantProviderInner>::ErrShift;
270        type Determinant = <$T as IDeterminantProviderInner>::Determinant;
271        type NicheExporter = <$T as IDeterminantProviderInner>::NicheExporter;
272        // type Debug = <$T as IDeterminantProviderInner>::Debug;
273    };
274    ($T: ty, $Trait: ty) => {
275        type OkShift = <$T as $Trait>::OkShift;
276        type ErrShift = <$T as $Trait>::ErrShift;
277        type Determinant = <$T as $Trait>::Determinant;
278        type NicheExporter = <$T as $Trait>::NicheExporter;
279        // type Debug = <$T as $Trait>::Debug;
280    };
281}
282
283impl<A: IStable, B: IStable> IDeterminantProvider<B> for A
284where
285    (A, B, <A::Size as Unsigned>::GreaterOrEq<B::Size>): IDeterminantProviderInnerRev,
286{
287    same_as!(
288        (A, B, <A::Size as Unsigned>::GreaterOrEq<B::Size>),
289        IDeterminantProviderInnerRev
290    );
291}
292
293// IF Ok::Size < Err::Size
294impl<Ok: IStable, Err: IStable> IDeterminantProviderInnerRev for (Ok, Err, B0)
295where
296    (Err, Ok, Ok::Size): IDeterminantProviderInner,
297{
298    type OkShift = <(Err, Ok, Ok::Size) as IDeterminantProviderInner>::ErrShift;
299    type ErrShift = U0;
300    type Determinant = Not<<(Err, Ok, Ok::Size) as IDeterminantProviderInner>::Determinant>;
301    type NicheExporter = <(Err, Ok, Ok::Size) as IDeterminantProviderInner>::NicheExporter;
302    // type Debug = <(Err, Ok, Ok::Size) as IDeterminantProviderInner>::Debug;
303}
304// ELSE
305impl<Ok: IStable, Err: IStable> IDeterminantProviderInnerRev for (Ok, Err, B1)
306where
307    (Ok, Err, Err::Size): IDeterminantProviderInner,
308{
309    type OkShift = U0;
310    same_as!((Ok, Err, Err::Size));
311}
312
313// IF Err::Size == 0
314mod err_non_empty;
315mod err_size_0;