1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
//! Closures: `(fn(EXINF), EXINF)`
use crate::abi;
use core::mem::MaybeUninit;

/// A bundle of a function pointer and associated data.
///
/// # Safety
///
/// When calling the function pointer, the second value must be provided as
/// the parameter.
///
/// If the original closure was `!Send`, it can only be called from the creator
/// thread.
pub type Closure = (unsafe extern "C" fn(abi::EXINF), abi::EXINF);

/// Conversion to [`Closure`].
pub trait IntoClosure {
    /// Convert `self` to `Closure`.
    fn into_closure(self) -> Closure;
}

/// Trivial conversion.
impl IntoClosure for (extern "C" fn(abi::EXINF), abi::EXINF) {
    #[inline]
    fn into_closure(self) -> Closure {
        (self.0, self.1)
    }
}

/// # Example
///
/// ```
/// use itron::closure::IntoClosure;
/// let (fp, data) = (|| dbg!()).into_closure();
/// unsafe { fp(data) };
///
/// let captured_value = 42u16;
/// let (fp, data) = (move || { assert_eq!(captured_value, 42); }).into_closure();
/// unsafe { fp(data) };
///
/// let captured_value = &"hello";
/// let (fp, data) = (move || { assert_eq!(*captured_value, "hello"); }).into_closure();
/// unsafe { fp(data) };
/// ```
///
/// The source type must fit in [`abi::EXINF`]:
///
/// ```compile_fail
/// # use itron::closure::IntoClosure;
/// let captured_value = [0usize; 2]; // too large!
/// let _ = (move || { dbg!(captured_value); }).into_closure();
/// ```
///
/// The source type must not contain a reference to a local variable:
///
/// ```compile_fail
/// # use itron::closure::IntoClosure;
/// let captured_value = 42usize;
/// let _ = (|| { dbg!(&captured_value); }).into_closure(); // capturing by reference
/// ```
impl<T: Fn() + Copy + 'static> IntoClosure for T {
    #[inline]
    fn into_closure(self) -> Closure {
        // Make sure `T` fits
        trait AssertSize {
            const X: ();
        }
        impl<T> AssertSize for T {
            const X: () = if core::mem::size_of::<T>() > core::mem::size_of::<abi::EXINF>() {
                let zero = 0;
                // compile-time panicking is not stable yet
                #[allow(unconditional_panic)]
                #[allow(non_snake_case)]
                let __T_is_too_large_to_fit_in_EXINF__ = 1 / zero;
                #[allow(clippy::empty_loop)]
                loop {}
            };
        }
        let () = <T as AssertSize>::X;

        extern "C" fn trampoline<T: Fn() + Copy + 'static>(x: abi::EXINF) {
            // Safety: `x` is a reinterpretation of the original `T`. This
            //         function reconstitutes `T` every time it's called, but
            //         this is safe because `T: Copy`.
            let t: T = unsafe { core::mem::transmute_copy(&x) };
            t();
        }

        // Makes sure the transmutation source type is large enough to
        // cover `EXINF` as required by `transmute_copy`.
        #[repr(C)]
        struct PadWithZero<T> {
            x: MaybeUninit<T>,
            zero: MaybeUninit<abi::EXINF>,
        }

        (trampoline::<T>, unsafe {
            core::mem::transmute_copy(&PadWithZero {
                x: MaybeUninit::new(self),
                zero: MaybeUninit::uninit(),
            })
        })
    }
}