stabby_abi/
result.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//! Stable results!
16
17use stabby_macros::tyeval;
18
19pub use crate::enums::IDeterminant;
20use crate::enums::IDeterminantProvider;
21use crate::istable::IBitMask;
22use crate::report::FieldReport;
23use crate::str::Str;
24use crate::unsigned::IUnsignedBase;
25use crate::{self as stabby, unreachable_unchecked, Bit, IStable, B0};
26use crate::{Alignment, Tuple, Unsigned};
27
28#[repr(transparent)]
29/// An ABI-stable, niche optimizing equivalent of [`core::result::Result`]
30pub struct Result<Ok, Err>
31where
32    Ok: IDeterminantProvider<Err>,
33    Err: IStable,
34{
35    storage: Storage<<Self as IStable>::Size, <Self as IStable>::Align>,
36}
37impl<Ok: Unpin, Err: Unpin> Unpin for Result<Ok, Err>
38where
39    Ok: IDeterminantProvider<Err>,
40    Err: IStable,
41{
42}
43type Determinant<Ok, Err> = <Ok as IDeterminantProvider<Err>>::Determinant;
44// SAFETY: See fields
45unsafe impl<Ok, Err> IStable for Result<Ok, Err>
46where
47    Ok: IDeterminantProvider<Err>,
48    Err: IStable,
49{
50    /// The size is the max of the variants' sizes, plus the size of the determinant upgraded to a multiple of the alignment.
51    type Size = tyeval!(<<Determinant<Ok, Err> as IStable>::Size as Unsigned>::NextMultipleOf<Self::Align> + <Ok::Size as Unsigned>::Max<Err::Size>);
52    /// The alignment is the max of the variants' alignments.
53    type Align = <Ok::Align as Alignment>::Max<Err::Align>;
54    // If either variant may contain an indirection, so may their sum-type.
55    type ContainsIndirections = <Ok::ContainsIndirections as Bit>::Or<Err::ContainsIndirections>;
56    // We trust the DeterminantProvider with this computation, it usually just discards the values for safety.
57    type ForbiddenValues =
58        <<Ok as IDeterminantProvider<Err>>::NicheExporter as IStable>::ForbiddenValues;
59    // OH NO! But we have lots of testing.
60    type UnusedBits = <<Tuple<Determinant<Ok, Err>, <Self::Align as Alignment>::AsUint> as IStable>::UnusedBits as IBitMask>::BitOr<<<<Ok as IDeterminantProvider<Err>>::NicheExporter as IStable>::UnusedBits as IBitMask>::Shift<<<Determinant<Ok, Err> as IStable>::Size as Unsigned>::NextMultipleOf<Self::Align>>>;
61    // Rust doesn't know `stabby` Results may have niches.
62    type HasExactlyOneNiche = B0;
63    #[cfg(feature = "experimental-ctypes")]
64    type CType = <Storage<<Self as IStable>::Size, <Self as IStable>::Align> as IStable>::CType;
65    const REPORT: &'static crate::report::TypeReport = &crate::report::TypeReport {
66        name: Str::new("Result"),
67        module: Str::new("stabby_abi::result"),
68        tyty: crate::report::TyTy::Enum(Str::new("stabby")),
69        version: 1,
70        fields: crate::StableLike::new(Some(&FieldReport {
71            name: Str::new("Ok"),
72            ty: Ok::REPORT,
73            next_field: crate::StableLike::new(Some(&FieldReport {
74                name: Str::new("Err"),
75                ty: Err::REPORT,
76                next_field: crate::StableLike::new(None),
77            })),
78        })),
79    };
80    const ID: u64 = crate::report::gen_id(Self::REPORT);
81}
82use seal::Storage;
83mod seal {
84    use core::mem::MaybeUninit;
85
86    use super::*;
87    #[stabby::stabby]
88    pub struct Storage<Size: Unsigned, Align: Alignment + Alignment> {
89        inner: MaybeUninit<<Align::Divide<Size> as IUnsignedBase>::Array<Align::AsUint>>,
90    }
91    impl<Size: Unsigned, Align: Alignment + Alignment> Storage<Size, Align> {
92        pub const fn zeroed() -> Self {
93            Self {
94                inner: MaybeUninit::zeroed(),
95            }
96        }
97    }
98}
99
100impl<Size: Unsigned, Align: Alignment + Alignment> Storage<Size, Align> {
101    const fn as_ptr(&self) -> *const u8 {
102        self as *const Self as *const _
103    }
104    #[rustversion::attr(since(1.86), const)]
105    fn as_mut_ptr(&mut self) -> *mut u8 {
106        self as *mut Self as *mut _
107    }
108}
109
110impl<Ok: Clone, Err: Clone> Clone for Result<Ok, Err>
111where
112    Ok: IDeterminantProvider<Err>,
113    Err: IStable,
114{
115    fn clone(&self) -> Self {
116        self.match_ref(|ok| Self::Ok(ok.clone()), |err| Self::Err(err.clone()))
117    }
118}
119impl<Ok, Err> core::fmt::Debug for Result<Ok, Err>
120where
121    Ok: IDeterminantProvider<Err>,
122    Err: IStable,
123    Ok: core::fmt::Debug,
124    Err: core::fmt::Debug,
125{
126    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
127        self.as_ref().fmt(f)
128    }
129}
130impl<Ok, Err> core::hash::Hash for Result<Ok, Err>
131where
132    Ok: IDeterminantProvider<Err>,
133    Err: IStable,
134    Ok: core::hash::Hash,
135    Err: core::hash::Hash,
136{
137    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
138        if self.is_ok() {
139            true.hash(state);
140            unsafe { self.ok_unchecked() }.hash(state);
141        } else {
142            false.hash(state);
143            unsafe { self.err_unchecked() }.hash(state);
144        }
145    }
146}
147impl<Ok, Err> core::cmp::PartialEq for Result<Ok, Err>
148where
149    Ok: IDeterminantProvider<Err>,
150    Err: IStable,
151    Ok: core::cmp::PartialEq,
152    Err: core::cmp::PartialEq,
153{
154    fn eq(&self, other: &Self) -> bool {
155        match (self.is_ok(), other.is_ok()) {
156            (true, true) => unsafe { self.ok_unchecked().eq(other.ok_unchecked()) },
157            (false, false) => unsafe { self.err_unchecked().eq(other.err_unchecked()) },
158            _ => false,
159        }
160    }
161}
162impl<Ok, Err> core::cmp::Eq for Result<Ok, Err>
163where
164    Ok: IDeterminantProvider<Err>,
165    Err: IStable,
166    Ok: core::cmp::Eq,
167    Err: core::cmp::Eq,
168{
169}
170impl<Ok, Err> From<core::result::Result<Ok, Err>> for Result<Ok, Err>
171where
172    Ok: IDeterminantProvider<Err>,
173    Err: IStable,
174{
175    fn from(value: core::result::Result<Ok, Err>) -> Self {
176        match value {
177            Ok(value) => Self::Ok(value),
178            Err(value) => Self::Err(value),
179        }
180    }
181}
182impl<Ok, Err> From<Result<Ok, Err>> for core::result::Result<Ok, Err>
183where
184    Ok: IDeterminantProvider<Err>,
185    Err: IStable,
186{
187    fn from(value: Result<Ok, Err>) -> Self {
188        value.match_owned(Ok, Err)
189    }
190}
191impl<Ok, Err> Drop for Result<Ok, Err>
192where
193    Ok: IDeterminantProvider<Err>,
194    Err: IStable,
195{
196    fn drop(&mut self) {
197        unsafe {
198            self.match_mut(
199                |mut ok| core::ptr::drop_in_place::<Ok>(&mut *ok),
200                |mut err| core::ptr::drop_in_place::<Err>(&mut *err),
201            )
202        }
203    }
204}
205impl<Ok, Err> Result<Ok, Err>
206where
207    Ok: IDeterminantProvider<Err>,
208    Err: IStable,
209{
210    const DET_SIZE: usize = <<<Determinant<Ok, Err> as IStable>::Size as Unsigned>::NextMultipleOf<
211        <Self as IStable>::Align,
212    > as Unsigned>::USIZE;
213    const OK_OFFSET: usize =
214        <<Ok as IDeterminantProvider<Err>>::OkShift as Unsigned>::USIZE + Self::DET_SIZE;
215    const ERR_OFFSET: usize =
216        <<Ok as IDeterminantProvider<Err>>::ErrShift as Unsigned>::USIZE + Self::DET_SIZE;
217    const fn ok_ptr(
218        storage: *const Storage<<Self as IStable>::Size, <Self as IStable>::Align>,
219    ) -> *const Ok {
220        unsafe { storage.cast::<u8>().add(Self::OK_OFFSET).cast() }
221    }
222    const fn ok_ptr_mut(
223        storage: *mut Storage<<Self as IStable>::Size, <Self as IStable>::Align>,
224    ) -> *mut Ok {
225        unsafe { storage.cast::<u8>().add(Self::OK_OFFSET).cast() }
226    }
227    const fn err_ptr(
228        storage: *const Storage<<Self as IStable>::Size, <Self as IStable>::Align>,
229    ) -> *const Err {
230        unsafe { storage.cast::<u8>().add(Self::ERR_OFFSET).cast() }
231    }
232    const fn err_ptr_mut(
233        storage: *mut Storage<<Self as IStable>::Size, <Self as IStable>::Align>,
234    ) -> *mut Err {
235        unsafe { storage.cast::<u8>().add(Self::ERR_OFFSET).cast() }
236    }
237    const fn det_ptr(
238        storage: *const Storage<<Self as IStable>::Size, <Self as IStable>::Align>,
239    ) -> *const Determinant<Ok, Err> {
240        storage.cast()
241    }
242    const fn det_ptr_mut(
243        storage: *mut Storage<<Self as IStable>::Size, <Self as IStable>::Align>,
244    ) -> *mut Determinant<Ok, Err> {
245        storage.cast()
246    }
247    /// Construct the `Ok` variant.
248    #[allow(non_snake_case)]
249    pub fn Ok(value: Ok) -> Self {
250        let mut storage = Storage::zeroed();
251        let storage_ptr = &mut storage as *mut _;
252        unsafe {
253            Self::ok_ptr_mut(storage_ptr).write(value);
254            Self::det_ptr_mut(storage_ptr).write(Determinant::<Ok, Err>::ok(storage_ptr.cast()));
255            Self { storage }
256        }
257    }
258    /// Construct the `Err` variant.
259    #[allow(non_snake_case)]
260    pub fn Err(value: Err) -> Self {
261        let mut storage = Storage::zeroed();
262        unsafe {
263            let storage_ptr = &mut storage as *mut _;
264            Self::err_ptr_mut(storage_ptr).write(value);
265            Self::det_ptr_mut(storage_ptr).write(Determinant::<Ok, Err>::err(storage_ptr.cast()));
266            Self { storage }
267        }
268    }
269    /// Converts to a standard [`Result`](core::result::Result) of immutable references to the variants.
270    #[allow(clippy::missing_errors_doc)]
271    pub fn as_ref(&self) -> core::result::Result<&Ok, &Err> {
272        self.match_ref(Ok, Err)
273    }
274
275    /// Equivalent to `match &self`. If you need multiple branches to obtain mutable access or ownership
276    /// of a local, use [`Self::match_ref_ctx`] instead.
277    pub fn match_ref<'a, U, FnOk: FnOnce(&'a Ok) -> U, FnErr: FnOnce(&'a Err) -> U>(
278        &'a self,
279        ok: FnOk,
280        err: FnErr,
281    ) -> U {
282        if self.is_ok() {
283            unsafe { ok(self.ok_unchecked()) }
284        } else {
285            unsafe { err(self.err_unchecked()) }
286        }
287    }
288    /// Equivalent to `match &self`.
289    pub fn match_ref_ctx<'a, T, U, FnOk: FnOnce(T, &'a Ok) -> U, FnErr: FnOnce(T, &'a Err) -> U>(
290        &'a self,
291        ctx: T,
292        ok: FnOk,
293        err: FnErr,
294    ) -> U {
295        if self.is_ok() {
296            unsafe { ok(ctx, self.ok_unchecked()) }
297        } else {
298            unsafe { err(ctx, self.err_unchecked()) }
299        }
300    }
301    /// Equivalent to `match &mut self`. If you need multiple branches to obtain mutable access or ownership
302    /// of a local, use [`Self::match_mut_ctx`] instead.
303    pub fn match_mut<
304        'a,
305        U,
306        FnOk: FnOnce(OkGuard<'a, Ok, Err>) -> U,
307        FnErr: FnOnce(ErrGuard<'a, Ok, Err>) -> U,
308    >(
309        &'a mut self,
310        ok: FnOk,
311        err: FnErr,
312    ) -> U {
313        let r;
314        if Self::is_ok(self) {
315            unsafe {
316                r = ok(self.ok_mut_unchecked());
317            }
318        } else {
319            unsafe {
320                r = err(self.err_mut_unchecked());
321            }
322        }
323        r
324    }
325    /// Equivalent to `match &mut self`.
326    pub fn match_mut_ctx<
327        'a,
328        T,
329        U,
330        FnOk: FnOnce(T, OkGuard<'a, Ok, Err>) -> U,
331        FnErr: FnOnce(T, ErrGuard<'_, Ok, Err>) -> U,
332    >(
333        &'a mut self,
334        ctx: T,
335        ok: FnOk,
336        err: FnErr,
337    ) -> U {
338        let r;
339        if Self::is_ok(self) {
340            unsafe {
341                r = ok(ctx, self.ok_mut_unchecked());
342            }
343        } else {
344            unsafe {
345                r = err(ctx, self.err_mut_unchecked());
346            }
347        }
348        r
349    }
350    /// Equivalent to `match self`. If you need multiple branches to obtain mutable access or ownership
351    /// of a local, use [`Self::match_owned_ctx`] instead.
352    pub fn match_owned<U, FnOk: FnOnce(Ok) -> U, FnErr: FnOnce(Err) -> U>(
353        self,
354        ok: FnOk,
355        err: FnErr,
356    ) -> U {
357        let is_ok = self.is_ok();
358        let storage = &self.storage;
359        if is_ok {
360            let t = unsafe { core::ptr::read(Self::ok_ptr(storage)) };
361            core::mem::forget(self);
362            ok(t)
363        } else {
364            let t = unsafe { core::ptr::read(Self::err_ptr(storage)) };
365            core::mem::forget(self);
366            err(t)
367        }
368    }
369    /// Equivalent to `match self`.
370    pub fn match_owned_ctx<U, T, FnOk: FnOnce(T, Ok) -> U, FnErr: FnOnce(T, Err) -> U>(
371        self,
372        ctx: T,
373        ok: FnOk,
374        err: FnErr,
375    ) -> U {
376        let is_ok = self.is_ok();
377        let storage = &self.storage;
378        if is_ok {
379            let t = unsafe { core::ptr::read(Self::ok_ptr(storage)) };
380            core::mem::forget(self);
381            ok(ctx, t)
382        } else {
383            let t = unsafe { core::ptr::read(Self::err_ptr(storage)) };
384            core::mem::forget(self);
385            err(ctx, t)
386        }
387    }
388    /// Returns `true` if in the `Ok` variant.
389    pub fn is_ok(&self) -> bool {
390        unsafe { &*Self::det_ptr(&self.storage) }.is_det_ok(self.storage.as_ptr())
391    }
392    /// Returns `true` if in the `Err` variant.
393    pub fn is_err(&self) -> bool {
394        !self.is_ok()
395    }
396    /// Returns the `Ok` variant if it exists, `None` otherwise.
397    pub fn ok(self) -> Option<Ok> {
398        self.match_owned(Some, |_| None)
399    }
400    /// Returns the `Err` variant if it exists, `None` otherwise.
401    pub fn err(self) -> Option<Err> {
402        self.match_owned(|_| None, Some)
403    }
404    /// Returns the `Ok` variant by reference if it exists, `None` otherwise.
405    pub fn ok_ref(&self) -> Option<&Ok> {
406        self.match_ref(Some, |_| None)
407    }
408    /// Returns the `Err` variant by reference if it exists, `None` otherwise.
409    pub fn err_ref(&self) -> Option<&Err> {
410        self.match_ref(|_| None, Some)
411    }
412    /// Returns the `Ok` variant by mutable reference if it exists, `None` otherwise.
413    pub fn ok_mut(&mut self) -> Option<OkGuard<'_, Ok, Err>> {
414        self.match_mut(Some, |_| None)
415    }
416    /// Returns the `Err` variant by mutable reference if it exists, `None` otherwise.
417    pub fn err_mut(&mut self) -> Option<ErrGuard<'_, Ok, Err>> {
418        self.match_mut(|_| None, Some)
419    }
420    /// Applies a computation to the `Ok` variant.
421    pub fn map<F: FnOnce(Ok) -> U, U>(self, f: F) -> Result<U, Err>
422    where
423        U: IDeterminantProvider<Err>,
424    {
425        self.match_owned(move |x| Result::Ok(f(x)), |x| Result::Err(x))
426    }
427    /// Applies a fallible computation to the `Err` variant.
428    pub fn and_then<F: FnOnce(Ok) -> Result<U, Err>, U>(self, f: F) -> Result<U, Err>
429    where
430        U: IDeterminantProvider<Err>,
431    {
432        self.match_owned(f, |x| Result::Err(x))
433    }
434    /// Returns the `Ok` variant if applicable, calling `f` on the `Err` otherwise.
435    pub fn unwrap_or_else<F: FnOnce(Err) -> Ok>(self, f: F) -> Ok {
436        self.match_owned(|x| x, f)
437    }
438    /// # Safety
439    /// Called on an `Err`, this triggers Undefined Behaviour.
440    pub unsafe fn unwrap_unchecked(self) -> Ok {
441        self.unwrap_or_else(|_| unsafe { unreachable_unchecked!() })
442    }
443    /// # Panics
444    /// If `!self.is_ok()`
445    pub fn unwrap(self) -> Ok
446    where
447        Err: core::fmt::Debug,
448    {
449        self.unwrap_or_else(|e| panic!("Result::unwrap called on Err variant: {e:?}"))
450    }
451    /// Returns the `Err` variant if applicable, calling `f` on the `Ok` otherwise.
452    pub fn unwrap_err_or_else<F: FnOnce(Ok) -> Err>(self, f: F) -> Err {
453        self.match_owned(f, |x| x)
454    }
455    /// # Safety
456    /// Called on an `Ok`, this triggers Undefined Behaviour.
457    pub unsafe fn unwrap_err_unchecked(self) -> Err {
458        self.unwrap_err_or_else(|_| unsafe { unreachable_unchecked!() })
459    }
460    /// # Panics
461    /// If `!self.is_err()`
462    pub fn unwrap_err(self) -> Err
463    where
464        Ok: core::fmt::Debug,
465    {
466        self.unwrap_err_or_else(|e| panic!("Result::unwrap_err called on Ok variant: {e:?}"))
467    }
468    const unsafe fn ok_unchecked(&self) -> &Ok {
469        &*Self::ok_ptr(&self.storage)
470    }
471    const unsafe fn err_unchecked(&self) -> &Err {
472        &*Self::err_ptr(&self.storage)
473    }
474    #[rustversion::attr(since(1.86), const)]
475    unsafe fn ok_mut_unchecked(&mut self) -> OkGuard<'_, Ok, Err> {
476        OkGuard { inner: self }
477    }
478    #[rustversion::attr(since(1.86), const)]
479    unsafe fn err_mut_unchecked(&mut self) -> ErrGuard<Ok, Err> {
480        ErrGuard { inner: self }
481    }
482}
483
484/// A guard that ensures that niche determinants are reinserted if the `Ok` variant of an [`Result`] is re-established after it may have been mutated.
485///
486/// When dropped, this guard ensures that the result's determinant is properly set.
487/// Failing to drop this guard may result in undefined behaviour.
488pub struct OkGuard<'a, Ok, Err>
489where
490    Ok: IDeterminantProvider<Err>,
491    Err: IStable,
492{
493    inner: &'a mut Result<Ok, Err>,
494}
495impl<Ok, Err> core::ops::Deref for OkGuard<'_, Ok, Err>
496where
497    Ok: IDeterminantProvider<Err>,
498    Err: IStable,
499{
500    type Target = Ok;
501    fn deref(&self) -> &Self::Target {
502        unsafe { self.inner.ok_unchecked() }
503    }
504}
505impl<Ok, Err> core::ops::DerefMut for OkGuard<'_, Ok, Err>
506where
507    Ok: IDeterminantProvider<Err>,
508    Err: IStable,
509{
510    fn deref_mut(&mut self) -> &mut Self::Target {
511        unsafe { &mut *Result::<Ok, Err>::ok_ptr_mut(&mut self.inner.storage) }
512    }
513}
514impl<Ok, Err> Drop for OkGuard<'_, Ok, Err>
515where
516    Ok: IDeterminantProvider<Err>,
517    Err: IStable,
518{
519    fn drop(&mut self) {
520        if <<Determinant<Ok, Err> as IDeterminant>::IsNicheTrick as Bit>::BOOL {
521            unsafe { Determinant::<Ok, Err>::ok(self.inner.storage.as_mut_ptr()) };
522        }
523    }
524}
525
526/// A guard that ensures that niche determinants are reinserted if the `Err` variant of an [`Option`] is re-established after it may have been mutated.
527///
528/// When dropped, this guard ensures that the result's determinant is properly set.
529/// Failing to drop this guard may result in undefined behaviour.
530pub struct ErrGuard<'a, Ok, Err>
531where
532    Ok: IDeterminantProvider<Err>,
533    Err: IStable,
534{
535    inner: &'a mut Result<Ok, Err>,
536}
537
538impl<Ok, Err> core::ops::Deref for ErrGuard<'_, Ok, Err>
539where
540    Ok: IDeterminantProvider<Err>,
541    Err: IStable,
542{
543    type Target = Err;
544    fn deref(&self) -> &Self::Target {
545        unsafe { self.inner.err_unchecked() }
546    }
547}
548impl<Ok, Err> core::ops::DerefMut for ErrGuard<'_, Ok, Err>
549where
550    Ok: IDeterminantProvider<Err>,
551    Err: IStable,
552{
553    fn deref_mut(&mut self) -> &mut Self::Target {
554        unsafe { &mut *Result::<Ok, Err>::err_ptr_mut(&mut self.inner.storage) }
555    }
556}
557impl<Ok, Err> Drop for ErrGuard<'_, Ok, Err>
558where
559    Ok: IDeterminantProvider<Err>,
560    Err: IStable,
561{
562    fn drop(&mut self) {
563        if <<Determinant<Ok, Err> as IDeterminant>::IsNicheTrick as Bit>::BOOL {
564            unsafe { Determinant::<Ok, Err>::err(self.inner.storage.as_mut_ptr()) };
565        }
566    }
567}
568
569#[cfg(feature = "serde")]
570mod serde_impl {
571    use super::*;
572    use serde::{Deserialize, Serialize};
573    impl<Ok: Serialize, Err: Serialize> Serialize for Result<Ok, Err>
574    where
575        Ok: IDeterminantProvider<Err>,
576        Err: IStable,
577    {
578        fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
579        where
580            S: serde::Serializer,
581        {
582            let this: core::result::Result<_, _> = self.as_ref();
583            this.serialize(serializer)
584        }
585    }
586    impl<'a, Ok: IDeterminantProvider<Err>, Err: IStable> Deserialize<'a> for Result<Ok, Err>
587    where
588        core::result::Result<Ok, Err>: Deserialize<'a>,
589    {
590        fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
591        where
592            D: serde::Deserializer<'a>,
593        {
594            Ok(core::result::Result::<Ok, Err>::deserialize(deserializer)?.into())
595        }
596    }
597}