Skip to main content

zr/
inline_fn.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::cell::UnsafeCell;
6use core::marker::PhantomData;
7use core::mem::{ManuallyDrop, MaybeUninit};
8use core::{fmt, ptr};
9
10/// Default inline storage capacity in bytes (3 machine words, e.g. 24 bytes on 64-bit platforms).
11pub const DEFAULT_INLINE_FN_SIZE: usize = 3 * core::mem::size_of::<usize>();
12
13/// Maximum alignment supported by inline function storage (8 bytes).
14const INLINE_STORAGE_ALIGN: usize = 8;
15
16#[repr(C, align(8))]
17struct AlignedStorage<const SIZE: usize> {
18    bytes: UnsafeCell<[MaybeUninit<u8>; SIZE]>,
19}
20
21const _: () = assert!(core::mem::align_of::<AlignedStorage<0>>() == INLINE_STORAGE_ALIGN);
22
23impl<const SIZE: usize> AlignedStorage<SIZE> {
24    #[inline]
25    const fn uninit() -> Self {
26        Self { bytes: UnsafeCell::new([MaybeUninit::uninit(); SIZE]) }
27    }
28
29    #[inline]
30    fn as_ptr(&self) -> *mut u8 {
31        self.bytes.get().cast::<u8>()
32    }
33}
34
35mod private {
36    pub trait SealedThreadSafety {}
37    pub trait SealedSig {}
38    pub trait SealedWithSig<Sig> {}
39}
40
41/// Marker type for closures that do not require `Send` or `Sync`.
42///
43/// An [`InlineFn`], [`InlineFnMut`], or [`InlineFnOnce`] parameterized with `Local` is `!Send` and
44/// `!Sync`, allowing it to capture thread-local state, raw pointers, or lock guards.
45pub struct Local(PhantomData<*mut ()>);
46
47impl fmt::Debug for Local {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.write_str("Local")
50    }
51}
52
53/// Marker type for closures that implement `Send` (but not necessarily `Sync`).
54///
55/// An [`InlineFn`], [`InlineFnMut`], or [`InlineFnOnce`] parameterized with `SendOnly` is `Send`
56/// and `!Sync`.
57pub struct SendOnly(PhantomData<UnsafeCell<()>>);
58
59impl fmt::Debug for SendOnly {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.write_str("SendOnly")
62    }
63}
64
65/// Marker type for closures that implement both `Send` and `Sync`.
66///
67/// An [`InlineFn`], [`InlineFnMut`], or [`InlineFnOnce`] parameterized with `SendSync` is both
68/// `Send` and `Sync`.
69pub struct SendSync(PhantomData<()>);
70
71impl fmt::Debug for SendSync {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        f.write_str("SendSync")
74    }
75}
76
77impl private::SealedThreadSafety for Local {}
78impl private::SealedThreadSafety for SendOnly {}
79impl private::SealedThreadSafety for SendSync {}
80
81/// Sealed trait enforcing the required `Send` / `Sync` bounds on a closure `F` for a given
82/// thread-safety marker (`Local`, `SendOnly`, or `SendSync`).
83///
84/// # Safety
85///
86/// - If `Self: Send`, any `F` for which `Self: ThreadSafety<F>` is implemented must satisfy
87///   `F: Send`.
88/// - If `Self: Sync`, any `F` for which `Self: ThreadSafety<F>` is implemented must satisfy
89///   `F: Send + Sync`.
90pub unsafe trait ThreadSafety<F>: private::SealedThreadSafety {}
91
92// SAFETY: `Local` is `!Send` and `!Sync`, so no thread-safety bounds on `F` are required.
93unsafe impl<F> ThreadSafety<F> for Local {}
94// SAFETY: `SendOnly` is `Send` and `!Sync`, and this impl requires `F: Send`.
95unsafe impl<F: Send> ThreadSafety<F> for SendOnly {}
96// SAFETY: `SendSync` is `Send` and `Sync`, and this impl requires `F: Send + Sync`.
97unsafe impl<F: Send + Sync> ThreadSafety<F> for SendSync {}
98
99/// Trait implemented for supported function pointer signature types (e.g., `fn(A, B) -> Ret`).
100pub trait FnSig: private::SealedSig {
101    #[doc(hidden)]
102    type Trampoline: Copy;
103}
104
105#[doc(hidden)]
106#[derive(Clone, Copy, Debug)]
107pub struct VTableOnce<Trampoline> {
108    call_once: Trampoline,
109    drop: unsafe fn(*mut u8),
110}
111
112#[doc(hidden)]
113#[derive(Clone, Copy, Debug)]
114pub struct VTable<Trampoline> {
115    call: Trampoline,
116    once: VTableOnce<Trampoline>,
117}
118
119trait HasDrop: Copy {
120    fn drop_fn(&self) -> unsafe fn(*mut u8);
121}
122
123impl<Trampoline: Copy> HasDrop for VTableOnce<Trampoline> {
124    #[inline]
125    fn drop_fn(&self) -> unsafe fn(*mut u8) {
126        self.drop
127    }
128}
129
130impl<Trampoline: Copy> HasDrop for VTable<Trampoline> {
131    #[inline]
132    fn drop_fn(&self) -> unsafe fn(*mut u8) {
133        self.once.drop
134    }
135}
136
137/// Trait implemented by closures that can be invoked once with signature `Sig`.
138///
139/// # Safety
140///
141/// - `VTABLE_ONCE.call_once` must be a valid trampoline for `Self` that, given a pointer to a
142///   properly aligned and initialized `Self` with exclusive ownership, moves `Self` out of the
143///   pointer and invokes it once.
144/// - `VTABLE_ONCE.drop` must be a valid drop function for `Self` that, given a pointer to a
145///   properly aligned and initialized `Self` with exclusive access, drops `Self` in place (or is a
146///   no-op if `!core::mem::needs_drop::<Self>()`).
147pub unsafe trait FnOnceWithSig<Sig: FnSig>: Sized + private::SealedWithSig<Sig> {
148    #[doc(hidden)]
149    const VTABLE_ONCE: VTableOnce<Sig::Trampoline>;
150}
151
152/// Trait implemented by closures that can be invoked mutably with signature `Sig`.
153///
154/// # Safety
155///
156/// - `VTABLE_MUT.call` must be a valid trampoline for `Self` that, given a pointer to a properly
157///   aligned and initialized `Self` with exclusive (`&mut Self`) access for the duration of the
158///   call, invokes `Self` mutably without moving or dropping it.
159/// - `VTABLE_MUT.once` must satisfy the safety requirements of [`FnOnceWithSig::VTABLE_ONCE`] for
160///   `Self`.
161pub unsafe trait FnMutWithSig<Sig: FnSig>: FnOnceWithSig<Sig> {
162    #[doc(hidden)]
163    const VTABLE_MUT: VTable<Sig::Trampoline>;
164}
165
166/// Trait implemented by closures that can be invoked by shared reference with signature `Sig`.
167///
168/// # Safety
169///
170/// - `VTABLE.call` must be a valid trampoline for `Self` that, given a pointer to a properly
171///   aligned and initialized `Self` with shared (`&Self`) access for the duration of the call,
172///   invokes `Self` by shared reference without mutating, moving, or dropping it.
173/// - `VTABLE.once` must satisfy the safety requirements of [`FnOnceWithSig::VTABLE_ONCE`] for
174///   `Self`.
175pub unsafe trait FnWithSig<Sig: FnSig>: FnMutWithSig<Sig> {
176    #[doc(hidden)]
177    const VTABLE: VTable<Sig::Trampoline>;
178}
179
180/// No-op drop function used for closure types `F` where `!core::mem::needs_drop::<F>()`.
181///
182/// # Safety
183///
184/// `_ptr` is not dereferenced, so no preconditions are required by `noop_drop` itself.
185unsafe fn noop_drop(_ptr: *mut u8) {}
186
187/// Drops an initialized instance of `F` in place at `ptr`.
188///
189/// # Safety
190///
191/// - `ptr` must be non-null, properly aligned for `F`, and valid for both reads and writes of `F`.
192/// - `ptr` must point to a valid, initialized instance of `F` with exclusive access.
193/// - The value at `ptr` must not be accessed or dropped again after this call returns.
194unsafe fn drop_in_place_fn<F>(ptr: *mut u8) {
195    // SAFETY: The caller guarantees `ptr` points to a valid, properly aligned, initialized
196    // instance of `F` with exclusive access, and that it is dropped at most once.
197    unsafe {
198        ptr::drop_in_place(ptr.cast::<F>());
199    }
200}
201
202const fn drop_fn_for<F>() -> unsafe fn(*mut u8) {
203    if core::mem::needs_drop::<F>() { drop_in_place_fn::<F> } else { noop_drop }
204}
205
206/// Shared storage and lifecycle management for [`InlineFn`], [`InlineFnMut`], and [`InlineFnOnce`].
207///
208/// # Safety Invariants
209///
210/// - `storage` contains a valid, initialized instance of some closure type `F` at offset 0, with
211///   `size_of::<F>() <= SIZE` and `align_of::<F>() <= INLINE_STORAGE_ALIGN`.
212/// - `F` outlives `'a` (`F: 'a`).
213/// - `S: ThreadSafety<F>` holds for the stored closure type `F` (so `S: Send` guarantees
214///   `F: Send`, and `S: Sync` guarantees `F: Send + Sync`).
215/// - `vtable.drop_fn()` is a valid drop function for the stored closure type `F` at
216///   `storage.as_ptr()` (satisfying the drop contract of [`FnOnceWithSig::VTABLE_ONCE`]), and any
217///   callable trampolines in `vtable` satisfy the safety invariant of the enclosing wrapper
218///   ([`InlineFn`], [`InlineFnMut`], or [`InlineFnOnce`]).
219/// - Operations taking `&Self` may only access the stored `F` via shared reference `&F`; mutating
220///   `F` requires `&mut Self`, and moving/consuming `F` requires taking `Self` by value and
221///   suppressing `RawInlineFn::drop` via `ManuallyDrop`.
222struct RawInlineFn<'a, const SIZE: usize, V: HasDrop, S> {
223    storage: AlignedStorage<SIZE>,
224    vtable: V,
225    _marker: PhantomData<(&'a (), S)>,
226}
227
228// SAFETY: By the type invariants of `RawInlineFn`, `S: ThreadSafety<F>` holds for the stored
229// closure `F`. When `S: Send` (`SendOnly` or `SendSync`), `ThreadSafety<F>` guarantees `F: Send`,
230// and `V: HasDrop` consists only of plain function pointers, so moving `RawInlineFn` across
231// threads is sound.
232unsafe impl<'a, const SIZE: usize, V: HasDrop, S: Send> Send for RawInlineFn<'a, SIZE, V, S> {}
233
234// SAFETY: By the type invariants of `RawInlineFn`, `S: ThreadSafety<F>` holds for the stored
235// closure `F`. When `S: Sync` (`SendSync`), `ThreadSafety<F>` guarantees `F: Send + Sync`, and
236// shared references `&RawInlineFn` only ever access the stored `F` via shared reference `&F`
237// (mutating `F` requires `&mut Self` and consuming `F` requires owned `Self`).
238unsafe impl<'a, const SIZE: usize, V: HasDrop, S: Sync> Sync for RawInlineFn<'a, SIZE, V, S> {}
239
240impl<'a, const SIZE: usize, V: HasDrop, S> Drop for RawInlineFn<'a, SIZE, V, S> {
241    #[inline]
242    fn drop(&mut self) {
243        // SAFETY: By the type invariants of `RawInlineFn`, `self.storage` holds a valid,
244        // initialized instance of the closure `F` matching `self.vtable.drop_fn()`, `&mut self`
245        // guarantees exclusive access, and `drop` is called at most once when `RawInlineFn` is
246        // destroyed.
247        unsafe {
248            (self.vtable.drop_fn())(self.as_ptr());
249        }
250    }
251}
252
253impl<'a, const SIZE: usize, V: HasDrop, S> RawInlineFn<'a, SIZE, V, S> {
254    /// Stores `closure` inline with `vtable`.
255    ///
256    /// # Safety
257    ///
258    /// `vtable.drop_fn()` must be a valid drop function for `F` (satisfying the drop contract of
259    /// [`FnOnceWithSig::VTABLE_ONCE`]), and any callable trampolines in `vtable` must be valid for
260    /// `F` under the invariants of the enclosing wrapper.
261    #[inline]
262    unsafe fn new<F>(closure: F, vtable: V) -> Self
263    where
264        F: 'a,
265        S: ThreadSafety<F>,
266    {
267        const {
268            assert!(
269                core::mem::size_of::<F>() <= SIZE,
270                "Closure state exceeds inline storage capacity"
271            );
272            assert!(
273                core::mem::align_of::<F>() <= INLINE_STORAGE_ALIGN,
274                "Closure alignment requirement exceeds inline storage alignment"
275            );
276        }
277
278        let storage = AlignedStorage::<SIZE>::uninit();
279        // SAFETY: We verified at compile time that `size_of::<F>() <= SIZE` and
280        // `align_of::<F>() <= align_of::<AlignedStorage<SIZE>>()`. `storage.as_ptr()` is
281        // valid for writes of `F`. `ptr::write` moves `closure` into `storage` without
282        // reading uninitialized padding bytes or dropping `closure`.
283        unsafe {
284            ptr::write(storage.as_ptr().cast::<F>(), closure);
285        }
286
287        Self { storage, vtable, _marker: PhantomData }
288    }
289
290    #[inline]
291    fn as_ptr(&self) -> *mut u8 {
292        self.storage.as_ptr()
293    }
294
295    #[inline]
296    fn resize<const NEW_SIZE: usize>(self) -> RawInlineFn<'a, NEW_SIZE, V, S> {
297        const {
298            assert!(
299                NEW_SIZE >= SIZE,
300                "Target inline storage capacity must be greater than or equal to source capacity"
301            );
302        }
303
304        let this = ManuallyDrop::new(self);
305        let storage = AlignedStorage::<NEW_SIZE>::uninit();
306        // SAFETY: `this.storage` and `storage` do not overlap, both are at least `SIZE` bytes
307        // (`NEW_SIZE >= SIZE`), and copying `MaybeUninit<u8>` bytes preserves the bitwise
308        // representation of the stored closure while `ManuallyDrop` prevents `RawInlineFn::drop`
309        // from running on `this`.
310        unsafe {
311            ptr::copy_nonoverlapping(
312                this.as_ptr().cast::<MaybeUninit<u8>>(),
313                storage.as_ptr().cast::<MaybeUninit<u8>>(),
314                SIZE,
315            );
316        }
317
318        RawInlineFn { storage, vtable: this.vtable, _marker: PhantomData }
319    }
320
321    /// Replaces the vtable and thread-safety marker of `self` without modifying the stored closure.
322    ///
323    /// # Safety
324    ///
325    /// - `new_vtable.drop_fn()` must be a valid drop function for the closure type `F` currently
326    ///   stored in `self`, and any callable trampolines in `new_vtable` must be valid for `F`
327    ///   under the invariants of the target wrapper.
328    /// - `NewS: ThreadSafety<F>` must hold for the closure type `F` currently stored in `self`
329    ///   (e.g., relaxing `SendSync` to `SendOnly` or `Local`, or keeping `S` unchanged).
330    #[inline]
331    unsafe fn map_vtable_and_safety<NewV: HasDrop, NewS>(
332        self,
333        new_vtable: NewV,
334    ) -> RawInlineFn<'a, SIZE, NewV, NewS> {
335        let this = ManuallyDrop::new(self);
336        // SAFETY: `this` is wrapped in `ManuallyDrop`, suppressing `RawInlineFn::drop`.
337        // `this.storage` is read once to move the underlying `AlignedStorage<SIZE>` into the
338        // returned `RawInlineFn`, preserving the initialized closure `F` at offset 0.
339        let storage = unsafe { ptr::read(&this.storage) };
340
341        RawInlineFn { storage, vtable: new_vtable, _marker: PhantomData }
342    }
343}
344
345/// An inline-stored, partially type-erased `Fn` closure with compile-time capacity `SIZE`.
346///
347/// Similar to C++ `fit::inline_function`, `InlineFn` stores the callable's captured environment
348/// directly inline in a fixed-size buffer of `SIZE` bytes without heap allocation. Attempting to
349/// construct an `InlineFn` from a closure whose size exceeds `SIZE` or alignment exceeds 8 bytes
350/// fails at compile time.
351///
352/// The `Sig` parameter specifies the function signature using standard Rust function pointer
353/// syntax, such as `fn() -> u64` or `fn(u32, bool) -> i32`.
354///
355/// The `S` parameter controls thread-safety bounds:
356/// - [`Local`] (default): `!Send + !Sync`, accepts any closure `F: Fn(...) + 'a`.
357/// - [`SendOnly`]: `Send + !Sync`, requires `F: Send`.
358/// - [`SendSync`]: `Send + Sync`, requires `F: Send + Sync`.
359pub struct InlineFn<'a, Sig: FnSig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE, S = Local> {
360    // Safety Invariant: `raw.vtable` satisfies the safety contract of `FnWithSig::VTABLE` for the
361    // closure `F` stored in `raw.storage` (in particular, `raw.vtable.call` only accesses `F` by
362    // shared reference `&F` without mutating, moving, or dropping it).
363    raw: RawInlineFn<'a, SIZE, VTable<Sig::Trampoline>, S>,
364}
365
366impl<'a, Sig: FnSig, const SIZE: usize, S> fmt::Debug for InlineFn<'a, Sig, SIZE, S> {
367    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368        f.debug_struct("InlineFn")
369            .field("size", &SIZE)
370            .field("drop", &(self.raw.vtable.once.drop as usize))
371            .finish()
372    }
373}
374
375impl<'a, Sig: FnSig, const SIZE: usize, S> InlineFn<'a, Sig, SIZE, S> {
376    /// Wraps `closure` inline, asserting at compile time that its size is `<= SIZE` and
377    /// alignment is `<= 8`.
378    #[inline]
379    pub fn new<F>(closure: F) -> Self
380    where
381        F: FnWithSig<Sig> + 'a,
382        S: ThreadSafety<F>,
383    {
384        // SAFETY: `F::VTABLE` is the vtable constructed for `F` by `FnWithSig<Sig>`, satisfying
385        // both `RawInlineFn::new` and `InlineFn`'s invariant on `raw.vtable`.
386        Self { raw: unsafe { RawInlineFn::new(closure, F::VTABLE) } }
387    }
388
389    /// Widens the inline storage capacity to `NEW_SIZE` bytes (`NEW_SIZE >= SIZE`).
390    ///
391    /// Fails to compile if `NEW_SIZE < SIZE`.
392    #[inline]
393    pub fn resize<const NEW_SIZE: usize>(self) -> InlineFn<'a, Sig, NEW_SIZE, S> {
394        InlineFn { raw: self.raw.resize::<NEW_SIZE>() }
395    }
396
397    /// Relaxes the thread-safety marker of this `InlineFn` to [`Local`] (`!Send + !Sync`).
398    #[inline]
399    pub fn into_local(self) -> InlineFn<'a, Sig, SIZE, Local> {
400        let vtable = self.raw.vtable;
401        // SAFETY: `vtable` is the existing valid `FnWithSig` vtable for the stored closure `F`,
402        // and `Local: ThreadSafety<F>` holds for all `F`.
403        InlineFn { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
404    }
405
406    /// Converts this shared `InlineFn` into a mutable [`InlineFnMut`] with the same capacity and
407    /// thread-safety bounds at zero cost.
408    #[inline]
409    pub fn into_fn_mut(self) -> InlineFnMut<'a, Sig, SIZE, S> {
410        let vtable = self.raw.vtable;
411        // SAFETY: `vtable` is the existing valid `FnWithSig` vtable for the stored closure `F`.
412        // Because `vtable.call` only accesses `F` via shared reference `&F`, it is also valid
413        // when invoked with exclusive `&mut F` access in `InlineFnMut`, and `S` is unchanged.
414        InlineFnMut { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
415    }
416
417    /// Converts this shared `InlineFn` into a single-use [`InlineFnOnce`] with the same capacity
418    /// and thread-safety bounds at zero cost.
419    #[inline]
420    pub fn into_fn_once(self) -> InlineFnOnce<'a, Sig, SIZE, S> {
421        let once_vtable = self.raw.vtable.once;
422        // SAFETY: `once_vtable` is the valid `VTableOnce` for the stored closure `F`, satisfying
423        // `InlineFnOnce`'s invariant on `raw.vtable`, and `S` is unchanged.
424        InlineFnOnce { raw: unsafe { self.raw.map_vtable_and_safety(once_vtable) } }
425    }
426}
427
428impl<'a, Sig: FnSig, const SIZE: usize> InlineFn<'a, Sig, SIZE, Local> {
429    /// Creates a local (`!Send + !Sync`) `InlineFn`.
430    #[inline]
431    pub fn new_local<F>(closure: F) -> Self
432    where
433        F: FnWithSig<Sig> + 'a,
434    {
435        Self::new(closure)
436    }
437}
438
439impl<'a, Sig: FnSig, const SIZE: usize> InlineFn<'a, Sig, SIZE, SendOnly> {
440    /// Creates a `Send` (`!Sync`) `InlineFn`.
441    #[inline]
442    pub fn new_send<F>(closure: F) -> Self
443    where
444        F: FnWithSig<Sig> + Send + 'a,
445    {
446        Self::new(closure)
447    }
448}
449
450impl<'a, Sig: FnSig, const SIZE: usize> InlineFn<'a, Sig, SIZE, SendSync> {
451    /// Creates a `Send + Sync` `InlineFn`.
452    #[inline]
453    pub fn new_send_sync<F>(closure: F) -> Self
454    where
455        F: FnWithSig<Sig> + Send + Sync + 'a,
456    {
457        Self::new(closure)
458    }
459
460    /// Relaxes the thread-safety marker from [`SendSync`] to [`SendOnly`].
461    #[inline]
462    pub fn into_send(self) -> InlineFn<'a, Sig, SIZE, SendOnly> {
463        let vtable = self.raw.vtable;
464        // SAFETY: `vtable` is the existing valid `FnWithSig` vtable for the stored closure `F`,
465        // and `SendSync: ThreadSafety<F>` implies `F: Send`, so `SendOnly: ThreadSafety<F>` holds.
466        InlineFn { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
467    }
468}
469
470impl<'a, Sig: FnSig, const SIZE: usize, S> From<InlineFn<'a, Sig, SIZE, S>>
471    for InlineFnMut<'a, Sig, SIZE, S>
472{
473    #[inline]
474    fn from(func: InlineFn<'a, Sig, SIZE, S>) -> Self {
475        func.into_fn_mut()
476    }
477}
478
479impl<'a, Sig: FnSig, const SIZE: usize, S> From<InlineFn<'a, Sig, SIZE, S>>
480    for InlineFnOnce<'a, Sig, SIZE, S>
481{
482    #[inline]
483    fn from(func: InlineFn<'a, Sig, SIZE, S>) -> Self {
484        func.into_fn_once()
485    }
486}
487
488/// An inline-stored, partially type-erased `FnMut` closure with compile-time capacity `SIZE`.
489///
490/// Like [`InlineFn`], `InlineFnMut` stores the callable inline without heap allocation, but
491/// takes `&mut self` on invocation, allowing the wrapped closure to mutate its captured state.
492pub struct InlineFnMut<'a, Sig: FnSig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE, S = Local> {
493    // Safety Invariant: `raw.vtable` satisfies the safety contract of `FnMutWithSig::VTABLE_MUT`
494    // (or `FnWithSig::VTABLE`) for the closure `F` stored in `raw.storage` (in particular,
495    // `raw.vtable.call` is valid to invoke when given exclusive `&mut F` access without moving or
496    // dropping `F`).
497    raw: RawInlineFn<'a, SIZE, VTable<Sig::Trampoline>, S>,
498}
499
500impl<'a, Sig: FnSig, const SIZE: usize, S> fmt::Debug for InlineFnMut<'a, Sig, SIZE, S> {
501    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
502        f.debug_struct("InlineFnMut")
503            .field("size", &SIZE)
504            .field("drop", &(self.raw.vtable.once.drop as usize))
505            .finish()
506    }
507}
508
509impl<'a, Sig: FnSig, const SIZE: usize, S> InlineFnMut<'a, Sig, SIZE, S> {
510    /// Wraps a mutable `closure` inline, asserting at compile time that its size is
511    /// `<= SIZE` and alignment is `<= 8`.
512    #[inline]
513    pub fn new<F>(closure: F) -> Self
514    where
515        F: FnMutWithSig<Sig> + 'a,
516        S: ThreadSafety<F>,
517    {
518        // SAFETY: `F::VTABLE_MUT` is the vtable constructed for `F` by `FnMutWithSig<Sig>`,
519        // satisfying both `RawInlineFn::new` and `InlineFnMut`'s invariant on `raw.vtable`.
520        Self { raw: unsafe { RawInlineFn::new(closure, F::VTABLE_MUT) } }
521    }
522
523    /// Widens the inline storage capacity to `NEW_SIZE` bytes (`NEW_SIZE >= SIZE`).
524    ///
525    /// Fails to compile if `NEW_SIZE < SIZE`.
526    #[inline]
527    pub fn resize<const NEW_SIZE: usize>(self) -> InlineFnMut<'a, Sig, NEW_SIZE, S> {
528        InlineFnMut { raw: self.raw.resize::<NEW_SIZE>() }
529    }
530
531    /// Relaxes the thread-safety marker of this `InlineFnMut` to [`Local`] (`!Send + !Sync`).
532    #[inline]
533    pub fn into_local(self) -> InlineFnMut<'a, Sig, SIZE, Local> {
534        let vtable = self.raw.vtable;
535        // SAFETY: `vtable` is the existing valid vtable for the stored closure `F`, and
536        // `Local: ThreadSafety<F>` holds for all `F`.
537        InlineFnMut { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
538    }
539
540    /// Converts this mutable `InlineFnMut` into a single-use [`InlineFnOnce`] with the same
541    /// capacity and thread-safety bounds at zero cost.
542    #[inline]
543    pub fn into_fn_once(self) -> InlineFnOnce<'a, Sig, SIZE, S> {
544        let once_vtable = self.raw.vtable.once;
545        // SAFETY: `once_vtable` is the valid `VTableOnce` for the stored closure `F`, satisfying
546        // `InlineFnOnce`'s invariant on `raw.vtable`, and `S` is unchanged.
547        InlineFnOnce { raw: unsafe { self.raw.map_vtable_and_safety(once_vtable) } }
548    }
549}
550
551impl<'a, Sig: FnSig, const SIZE: usize> InlineFnMut<'a, Sig, SIZE, Local> {
552    /// Creates a local (`!Send + !Sync`) `InlineFnMut`.
553    #[inline]
554    pub fn new_local<F>(closure: F) -> Self
555    where
556        F: FnMutWithSig<Sig> + 'a,
557    {
558        Self::new(closure)
559    }
560}
561
562impl<'a, Sig: FnSig, const SIZE: usize> InlineFnMut<'a, Sig, SIZE, SendOnly> {
563    /// Creates a `Send` (`!Sync`) `InlineFnMut`.
564    #[inline]
565    pub fn new_send<F>(closure: F) -> Self
566    where
567        F: FnMutWithSig<Sig> + Send + 'a,
568    {
569        Self::new(closure)
570    }
571}
572
573impl<'a, Sig: FnSig, const SIZE: usize> InlineFnMut<'a, Sig, SIZE, SendSync> {
574    /// Creates a `Send + Sync` `InlineFnMut`.
575    #[inline]
576    pub fn new_send_sync<F>(closure: F) -> Self
577    where
578        F: FnMutWithSig<Sig> + Send + Sync + 'a,
579    {
580        Self::new(closure)
581    }
582
583    /// Relaxes the thread-safety marker from [`SendSync`] to [`SendOnly`].
584    #[inline]
585    pub fn into_send(self) -> InlineFnMut<'a, Sig, SIZE, SendOnly> {
586        let vtable = self.raw.vtable;
587        // SAFETY: `vtable` is the existing valid vtable for the stored closure `F`, and
588        // `SendSync: ThreadSafety<F>` implies `F: Send`, so `SendOnly: ThreadSafety<F>` holds.
589        InlineFnMut { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
590    }
591}
592
593impl<'a, Sig: FnSig, const SIZE: usize, S> From<InlineFnMut<'a, Sig, SIZE, S>>
594    for InlineFnOnce<'a, Sig, SIZE, S>
595{
596    #[inline]
597    fn from(func: InlineFnMut<'a, Sig, SIZE, S>) -> Self {
598        func.into_fn_once()
599    }
600}
601
602/// An inline-stored, partially type-erased `FnOnce` closure with compile-time capacity `SIZE`.
603///
604/// Similar to C++ `fit::inline_callback`, `InlineFnOnce` stores a single-use callable inline
605/// without heap allocation and consumes `self` upon invocation.
606pub struct InlineFnOnce<'a, Sig: FnSig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE, S = Local> {
607    // Safety Invariant: `raw.vtable` satisfies the safety contract of `FnOnceWithSig::VTABLE_ONCE`
608    // for the closure `F` stored in `raw.storage` (in particular, `raw.vtable.call_once` is valid
609    // to invoke once when moving/consuming `F` with exclusive ownership).
610    raw: RawInlineFn<'a, SIZE, VTableOnce<Sig::Trampoline>, S>,
611}
612
613impl<'a, Sig: FnSig, const SIZE: usize, S> fmt::Debug for InlineFnOnce<'a, Sig, SIZE, S> {
614    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615        f.debug_struct("InlineFnOnce")
616            .field("size", &SIZE)
617            .field("drop", &(self.raw.vtable.drop as usize))
618            .finish()
619    }
620}
621
622impl<'a, Sig: FnSig, const SIZE: usize, S> InlineFnOnce<'a, Sig, SIZE, S> {
623    /// Wraps a single-use `closure` inline, asserting at compile time that its size is
624    /// `<= SIZE` and alignment is `<= 8`.
625    #[inline]
626    pub fn new<F>(closure: F) -> Self
627    where
628        F: FnOnceWithSig<Sig> + 'a,
629        S: ThreadSafety<F>,
630    {
631        // SAFETY: `F::VTABLE_ONCE` is the vtable constructed for `F` by `FnOnceWithSig<Sig>`,
632        // satisfying both `RawInlineFn::new` and `InlineFnOnce`'s invariant on `raw.vtable`.
633        Self { raw: unsafe { RawInlineFn::new(closure, F::VTABLE_ONCE) } }
634    }
635
636    /// Widens the inline storage capacity to `NEW_SIZE` bytes (`NEW_SIZE >= SIZE`).
637    ///
638    /// Fails to compile if `NEW_SIZE < SIZE`.
639    #[inline]
640    pub fn resize<const NEW_SIZE: usize>(self) -> InlineFnOnce<'a, Sig, NEW_SIZE, S> {
641        InlineFnOnce { raw: self.raw.resize::<NEW_SIZE>() }
642    }
643
644    /// Relaxes the thread-safety marker of this `InlineFnOnce` to [`Local`] (`!Send + !Sync`).
645    #[inline]
646    pub fn into_local(self) -> InlineFnOnce<'a, Sig, SIZE, Local> {
647        let vtable = self.raw.vtable;
648        // SAFETY: `vtable` is the existing valid `VTableOnce` for the stored closure `F`, and
649        // `Local: ThreadSafety<F>` holds for all `F`.
650        InlineFnOnce { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
651    }
652}
653
654impl<'a, Sig: FnSig, const SIZE: usize> InlineFnOnce<'a, Sig, SIZE, Local> {
655    /// Creates a local (`!Send + !Sync`) `InlineFnOnce`.
656    #[inline]
657    pub fn new_local<F>(closure: F) -> Self
658    where
659        F: FnOnceWithSig<Sig> + 'a,
660    {
661        Self::new(closure)
662    }
663}
664
665impl<'a, Sig: FnSig, const SIZE: usize> InlineFnOnce<'a, Sig, SIZE, SendOnly> {
666    /// Creates a `Send` (`!Sync`) `InlineFnOnce`.
667    #[inline]
668    pub fn new_send<F>(closure: F) -> Self
669    where
670        F: FnOnceWithSig<Sig> + Send + 'a,
671    {
672        Self::new(closure)
673    }
674}
675
676impl<'a, Sig: FnSig, const SIZE: usize> InlineFnOnce<'a, Sig, SIZE, SendSync> {
677    /// Creates a `Send + Sync` `InlineFnOnce`.
678    #[inline]
679    pub fn new_send_sync<F>(closure: F) -> Self
680    where
681        F: FnOnceWithSig<Sig> + Send + Sync + 'a,
682    {
683        Self::new(closure)
684    }
685
686    /// Relaxes the thread-safety marker from [`SendSync`] to [`SendOnly`].
687    #[inline]
688    pub fn into_send(self) -> InlineFnOnce<'a, Sig, SIZE, SendOnly> {
689        let vtable = self.raw.vtable;
690        // SAFETY: `vtable` is the existing valid `VTableOnce` for the stored closure `F`, and
691        // `SendSync: ThreadSafety<F>` implies `F: Send`, so `SendOnly: ThreadSafety<F>` holds.
692        InlineFnOnce { raw: unsafe { self.raw.map_vtable_and_safety(vtable) } }
693    }
694}
695
696/// Type alias for an [`InlineFn`] that is `Send` (but `!Sync`).
697pub type SendInlineFn<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
698    InlineFn<'a, Sig, SIZE, SendOnly>;
699
700/// Type alias for an [`InlineFn`] that is both `Send` and `Sync`.
701pub type SendSyncInlineFn<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
702    InlineFn<'a, Sig, SIZE, SendSync>;
703
704/// Type alias for an [`InlineFnMut`] that is `Send` (but `!Sync`).
705pub type SendInlineFnMut<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
706    InlineFnMut<'a, Sig, SIZE, SendOnly>;
707
708/// Type alias for an [`InlineFnMut`] that is both `Send` and `Sync`.
709pub type SendSyncInlineFnMut<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
710    InlineFnMut<'a, Sig, SIZE, SendSync>;
711
712/// Type alias for an [`InlineFnOnce`] that is `Send` (but `!Sync`).
713pub type SendInlineFnOnce<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
714    InlineFnOnce<'a, Sig, SIZE, SendOnly>;
715
716/// Type alias for an [`InlineFnOnce`] that is both `Send` and `Sync`.
717pub type SendSyncInlineFnOnce<'a, Sig, const SIZE: usize = DEFAULT_INLINE_FN_SIZE> =
718    InlineFnOnce<'a, Sig, SIZE, SendSync>;
719
720macro_rules! impl_inline_fn_for_signature {
721    ($($arg:ident : $arg_ty:ident),*) => {
722        impl<$($arg_ty,)* Ret> private::SealedSig for fn($($arg_ty),*) -> Ret {}
723
724        impl<$($arg_ty,)* Ret> FnSig for fn($($arg_ty),*) -> Ret {
725            type Trampoline = unsafe fn(*mut u8 $(, $arg_ty)*) -> Ret;
726        }
727
728        impl<$($arg_ty,)* Ret, F> private::SealedWithSig<fn($($arg_ty),*) -> Ret> for F
729        where
730            F: FnOnce($($arg_ty),*) -> Ret,
731        {
732        }
733
734        // SAFETY: `call_once_trampoline` moves `F` out of `state_ptr` and invokes it once, and
735        // `drop_fn_for::<F>()` drops `F` in place at `state_ptr`.
736        unsafe impl<$($arg_ty,)* Ret, F> FnOnceWithSig<fn($($arg_ty),*) -> Ret> for F
737        where
738            F: FnOnce($($arg_ty),*) -> Ret,
739        {
740            const VTABLE_ONCE: VTableOnce<unsafe fn(*mut u8 $(, $arg_ty)*) -> Ret> = {
741                /// Moves `F` out of `state_ptr` and invokes it once.
742                ///
743                /// # Safety
744                ///
745                /// - `state_ptr` must be non-null, properly aligned for `F`, and valid for reads
746                ///   of `F`.
747                /// - `state_ptr` must point to a valid, initialized instance of `F` with exclusive
748                ///   ownership.
749                /// - Ownership of `F` is consumed by this call; the value at `state_ptr` must not
750                ///   be accessed or dropped again after this call.
751                #[allow(clippy::too_many_arguments)]
752                unsafe fn call_once_trampoline<$($arg_ty,)* Ret, F: FnOnce($($arg_ty),*) -> Ret>(
753                    state_ptr: *mut u8,
754                    $($arg: $arg_ty),*
755                ) -> Ret {
756                    // SAFETY: The caller guarantees `state_ptr` points to a valid, properly
757                    // aligned, initialized `F` and transfers ownership of `F` to this call so it
758                    // is moved and consumed exactly once.
759                    let closure = unsafe { ptr::read(state_ptr.cast::<F>()) };
760                    closure($($arg),*)
761                }
762
763                VTableOnce {
764                    call_once: call_once_trampoline::<$($arg_ty,)* Ret, F>,
765                    drop: drop_fn_for::<F>(),
766                }
767            };
768        }
769
770        // SAFETY: `call_mut_trampoline` invokes `F` via `&mut F`, and `VTABLE_ONCE` is `F`'s
771        // valid `FnOnceWithSig` vtable.
772        unsafe impl<$($arg_ty,)* Ret, F> FnMutWithSig<fn($($arg_ty),*) -> Ret> for F
773        where
774            F: FnMut($($arg_ty),*) -> Ret,
775        {
776            const VTABLE_MUT: VTable<unsafe fn(*mut u8 $(, $arg_ty)*) -> Ret> = {
777                /// Invokes `F` at `state_ptr` by mutable reference `&mut F`.
778                ///
779                /// # Safety
780                ///
781                /// - `state_ptr` must be non-null, properly aligned for `F`, and valid for reads
782                ///   and writes of `F`.
783                /// - `state_ptr` must point to a valid, initialized instance of `F` with exclusive
784                ///   (`&mut F`) access for the duration of this call.
785                #[allow(clippy::too_many_arguments)]
786                unsafe fn call_mut_trampoline<$($arg_ty,)* Ret, F: FnMut($($arg_ty),*) -> Ret>(
787                    state_ptr: *mut u8,
788                    $($arg: $arg_ty),*
789                ) -> Ret {
790                    // SAFETY: The caller guarantees `state_ptr` points to a valid, properly
791                    // aligned, initialized `F` with exclusive access for the duration of the call.
792                    let closure_mut = unsafe { &mut *state_ptr.cast::<F>() };
793                    closure_mut($($arg),*)
794                }
795
796                VTable {
797                    call: call_mut_trampoline::<$($arg_ty,)* Ret, F>,
798                    once: <F as FnOnceWithSig<fn($($arg_ty),*) -> Ret>>::VTABLE_ONCE,
799                }
800            };
801        }
802
803        // SAFETY: `call_trampoline` invokes `F` via `&F`, and `VTABLE_ONCE` is `F`'s valid
804        // `FnOnceWithSig` vtable.
805        unsafe impl<$($arg_ty,)* Ret, F> FnWithSig<fn($($arg_ty),*) -> Ret> for F
806        where
807            F: Fn($($arg_ty),*) -> Ret,
808        {
809            const VTABLE: VTable<unsafe fn(*mut u8 $(, $arg_ty)*) -> Ret> = {
810                /// Invokes `F` at `state_ptr` by shared reference `&F`.
811                ///
812                /// # Safety
813                ///
814                /// - `state_ptr` must be non-null, properly aligned for `F`, and valid for reads
815                ///   of `F`.
816                /// - `state_ptr` must point to a valid, initialized instance of `F` with shared
817                ///   (`&F`) access (no concurrent mutable access) for the duration of this call.
818                #[allow(clippy::too_many_arguments)]
819                unsafe fn call_trampoline<$($arg_ty,)* Ret, F: Fn($($arg_ty),*) -> Ret>(
820                    state_ptr: *mut u8,
821                    $($arg: $arg_ty),*
822                ) -> Ret {
823                    // SAFETY: The caller guarantees `state_ptr` points to a valid, properly
824                    // aligned, initialized `F` with shared access for the duration of the call.
825                    let closure_ref = unsafe { &*state_ptr.cast::<F>() };
826                    closure_ref($($arg),*)
827                }
828
829                VTable {
830                    call: call_trampoline::<$($arg_ty,)* Ret, F>,
831                    once: <F as FnOnceWithSig<fn($($arg_ty),*) -> Ret>>::VTABLE_ONCE,
832                }
833            };
834        }
835
836        impl<'a, $($arg_ty,)* Ret, const SIZE: usize, S>
837            InlineFn<'a, fn($($arg_ty),*) -> Ret, SIZE, S>
838        {
839            /// Invokes the underlying closure with the provided arguments.
840            #[inline]
841            #[allow(clippy::too_many_arguments)]
842            pub fn call(&self, $($arg: $arg_ty),*) -> Ret {
843                // SAFETY: By the type invariants of `RawInlineFn` and `InlineFn`, `self.raw`
844                // holds a valid, initialized `F` matching `self.raw.vtable.call`, and `&self`
845                // guarantees shared access to `F` for the duration of the call.
846                unsafe { (self.raw.vtable.call)(self.raw.as_ptr() $(, $arg)*) }
847            }
848        }
849
850        impl<'a, $($arg_ty,)* Ret, const SIZE: usize, S>
851            InlineFnMut<'a, fn($($arg_ty),*) -> Ret, SIZE, S>
852        {
853            /// Invokes the underlying mutable closure with the provided arguments.
854            #[inline]
855            #[allow(clippy::too_many_arguments)]
856            pub fn call(&mut self, $($arg: $arg_ty),*) -> Ret {
857                // SAFETY: By the type invariants of `RawInlineFn` and `InlineFnMut`, `self.raw`
858                // holds a valid, initialized `F` matching `self.raw.vtable.call`, and `&mut self`
859                // guarantees exclusive access to `F` for the duration of the call.
860                unsafe { (self.raw.vtable.call)(self.raw.as_ptr() $(, $arg)*) }
861            }
862        }
863
864        impl<'a, $($arg_ty,)* Ret, const SIZE: usize, S>
865            InlineFnOnce<'a, fn($($arg_ty),*) -> Ret, SIZE, S>
866        {
867            /// Consumes `self` and invokes the underlying closure once.
868            #[inline]
869            #[allow(clippy::too_many_arguments)]
870            pub fn call(self, $($arg: $arg_ty),*) -> Ret {
871                let raw = ManuallyDrop::new(self.raw);
872                // SAFETY: By the type invariants of `RawInlineFn` and `InlineFnOnce`, `raw` holds
873                // a valid, initialized `F` matching `raw.vtable.call_once`. Wrapping `self.raw` in
874                // `ManuallyDrop` suppresses `RawInlineFn::drop` so `F` is moved and consumed
875                // exactly once by `call_once`.
876                unsafe { (raw.vtable.call_once)(raw.as_ptr() $(, $arg)*) }
877            }
878        }
879    };
880}
881
882impl_inline_fn_for_signature!();
883impl_inline_fn_for_signature!(a1: A1);
884impl_inline_fn_for_signature!(a1: A1, a2: A2);
885impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3);
886impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4);
887impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4, a5: A5);
888impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6);
889impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7);
890impl_inline_fn_for_signature!(a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8);
891
892#[cfg(test)]
893mod tests {
894    use super::*;
895    use core::cell::Cell;
896    use core::fmt::Write as _;
897    use core::sync::atomic::{AtomicUsize, Ordering};
898
899    #[test]
900    fn test_inline_fn_zero_and_multiple_args() {
901        let f0: InlineFn<'_, fn() -> u64> = InlineFn::new(|| 42);
902        assert_eq!(f0.call(), 42);
903
904        let factor = 3u32;
905        let f2: InlineFn<'_, fn(u32, u32) -> u32> = InlineFn::new(|a, b| (a + b) * factor);
906        assert_eq!(f2.call(4, 5), 27);
907        assert_eq!(f2.call(1, 2), 9);
908
909        let f8: InlineFn<'_, fn(u32, u32, u32, u32, u32, u32, u32, u32) -> u32> =
910            InlineFn::new(|a1, a2, a3, a4, a5, a6, a7, a8| a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8);
911        assert_eq!(f8.call(1, 2, 3, 4, 5, 6, 7, 8), 36);
912    }
913
914    #[test]
915    fn test_zero_sized_closure_and_non_static_refs() {
916        let zst_fn: InlineFn<'_, fn(u32) -> u32, 0> = InlineFn::new(|x: u32| x + 1);
917        assert_eq!(zst_fn.call(9), 10);
918
919        fn call_with_local_ref<'a, 'b>(local_val: &'a u32, arg: &'b u32) -> u32 {
920            let borrow_fn: InlineFn<'a, fn(&'b u32) -> u32> =
921                InlineFn::new(|r: &'b u32| *r + *local_val);
922            borrow_fn.call(arg)
923        }
924
925        let local_val = 100u32;
926        let arg = 23u32;
927        assert_eq!(call_with_local_ref(&local_val, &arg), 123);
928    }
929
930    #[test]
931    fn test_zst_with_drop_and_resize() {
932        static ZST_DROPS: AtomicUsize = AtomicUsize::new(0);
933        struct ZstGuard;
934        impl Drop for ZstGuard {
935            fn drop(&mut self) {
936                ZST_DROPS.fetch_add(1, Ordering::SeqCst);
937            }
938        }
939
940        ZST_DROPS.store(0, Ordering::SeqCst);
941        {
942            let guard = ZstGuard;
943            let f: InlineFn<'static, fn() -> u32, 0> = InlineFn::new(move || {
944                let _ = &guard;
945                7
946            });
947            assert_eq!(f.call(), 7);
948            let widened: InlineFn<'static, fn() -> u32, 8> = f.resize::<8>();
949            assert_eq!(widened.call(), 7);
950            assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 0);
951        }
952        assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 1);
953
954        {
955            let guard = ZstGuard;
956            let f_once: InlineFnOnce<'static, fn() -> u32, 0> = InlineFnOnce::new(move || {
957                drop(guard);
958                11
959            });
960            let widened_once: InlineFnOnce<'static, fn() -> u32, 8> = f_once.resize::<8>();
961            assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 1);
962            assert_eq!(widened_once.call(), 11);
963            assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 2);
964        }
965        assert_eq!(ZST_DROPS.load(Ordering::SeqCst), 2);
966    }
967
968    #[test]
969    fn test_inline_fn_mut() {
970        let mut sum = 0u64;
971        {
972            let mut f: InlineFnMut<'_, fn(u64) -> u64> = InlineFnMut::new(|x| {
973                sum += x;
974                sum
975            });
976            assert_eq!(f.call(10), 10);
977            assert_eq!(f.call(25), 35);
978        }
979        assert_eq!(sum, 35);
980    }
981
982    #[test]
983    fn test_inline_fn_once_consumes_and_drops_once() {
984        struct DropCounter<'a>(&'a Cell<usize>, u32);
985        impl Drop for DropCounter<'_> {
986            fn drop(&mut self) {
987                self.0.set(self.0.get() + 1);
988            }
989        }
990
991        let drops = Cell::new(0);
992        let token = DropCounter(&drops, 99);
993        let f: InlineFnOnce<'_, fn(u32) -> u32> = InlineFnOnce::new(move |x| {
994            let t = token;
995            t.1 + x
996        });
997        assert_eq!(drops.get(), 0);
998        assert_eq!(f.call(1), 100);
999        assert_eq!(drops.get(), 1);
1000    }
1001
1002    #[test]
1003    fn test_drop_without_calling() {
1004        struct DropCounter<'a>(&'a Cell<usize>, [u64; 2]);
1005        impl Drop for DropCounter<'_> {
1006            fn drop(&mut self) {
1007                self.0.set(self.0.get() + 1);
1008            }
1009        }
1010
1011        let drops = Cell::new(0);
1012        {
1013            let token = DropCounter(&drops, [1, 2]);
1014            let _f: InlineFn<'_, fn() -> u64> = InlineFn::new(move || token.1[0] + token.1[1]);
1015            assert_eq!(drops.get(), 0);
1016        }
1017        assert_eq!(drops.get(), 1);
1018
1019        {
1020            let token = DropCounter(&drops, [3, 4]);
1021            let _f_once: InlineFnOnce<'_, fn() -> u64> =
1022                InlineFnOnce::new(move || token.1[0] + token.1[1]);
1023            assert_eq!(drops.get(), 1);
1024        }
1025        assert_eq!(drops.get(), 2);
1026    }
1027
1028    #[test]
1029    fn test_custom_sizes_and_resize() {
1030        struct DropCounter<'a>(&'a Cell<usize>, [u64; 2]);
1031        impl Drop for DropCounter<'_> {
1032            fn drop(&mut self) {
1033                self.0.set(self.0.get() + 1);
1034            }
1035        }
1036
1037        let data = [10u64, 20, 30, 40, 50, 60];
1038        let small: InlineFn<'_, fn() -> u64, 48> = InlineFn::new(move || data.iter().sum());
1039        assert_eq!(small.call(), 210);
1040
1041        let same_size: InlineFn<'_, fn() -> u64, 48> = small.resize::<48>();
1042        assert_eq!(same_size.call(), 210);
1043
1044        let larger: InlineFn<'_, fn() -> u64, 64> = same_size.resize::<64>();
1045        assert_eq!(larger.call(), 210);
1046
1047        // Verify `InlineFnMut::resize` and `InlineFnOnce::resize` preserve state and drop once.
1048        let drops = Cell::new(0);
1049        {
1050            let mut token = DropCounter(&drops, [5, 10]);
1051            let mut f_mut: InlineFnMut<'_, fn(u64) -> u64, 24> = InlineFnMut::new(move |x| {
1052                token.1[0] += x;
1053                token.1[0] + token.1[1]
1054            });
1055            assert_eq!(f_mut.call(3), 18);
1056            let mut f_mut_wide: InlineFnMut<'_, fn(u64) -> u64, 48> = f_mut.resize::<48>();
1057            assert_eq!(drops.get(), 0);
1058            assert_eq!(f_mut_wide.call(2), 20);
1059        }
1060        assert_eq!(drops.get(), 1);
1061
1062        {
1063            let token = DropCounter(&drops, [20, 30]);
1064            let f_once: InlineFnOnce<'_, fn(u64) -> u64, 24> = InlineFnOnce::new(move |x| {
1065                let t = token;
1066                t.1[0] + t.1[1] + x
1067            });
1068            let f_once_wide: InlineFnOnce<'_, fn(u64) -> u64, 48> = f_once.resize::<48>();
1069            assert_eq!(drops.get(), 1);
1070            assert_eq!(f_once_wide.call(4), 54);
1071            assert_eq!(drops.get(), 2);
1072        }
1073    }
1074
1075    #[test]
1076    fn test_align8_with_padding() {
1077        #[repr(C, align(8))]
1078        struct Padded<'a> {
1079            tag: u8,
1080            // 7 bytes of uninitialized padding between `tag` and `value`.
1081            value: u64,
1082            drops: &'a Cell<usize>,
1083        }
1084
1085        impl Drop for Padded<'_> {
1086            fn drop(&mut self) {
1087                self.drops.set(self.drops.get() + 1);
1088            }
1089        }
1090
1091        let drops = Cell::new(0);
1092        {
1093            let p = Padded { tag: 3, value: 100, drops: &drops };
1094            let f: InlineFn<'_, fn(u64) -> u64, 24> =
1095                InlineFn::new(move |x| (p.tag as u64) + p.value + x);
1096            let widened = f.resize::<32>();
1097            assert_eq!(widened.call(7), 110);
1098            assert_eq!(drops.get(), 0);
1099        }
1100        assert_eq!(drops.get(), 1);
1101    }
1102
1103    #[test]
1104    fn test_reentrant_inline_fn_call() {
1105        type RecFn<'a> = InlineFn<'a, fn(u32) -> u32>;
1106        let depth = Cell::new(0u32);
1107        let self_ptr: Cell<*const RecFn<'_>> = Cell::new(core::ptr::null());
1108        let f: RecFn<'_> = InlineFn::new(|n| {
1109            depth.set(depth.get() + 1);
1110            if n == 0 {
1111                0
1112            } else {
1113                // SAFETY: `self_ptr` points to `f`, which remains live for the duration of `f.call`.
1114                let me = unsafe { &*self_ptr.get() };
1115                n + me.call(n - 1)
1116            }
1117        });
1118        self_ptr.set(&f);
1119        assert_eq!(f.call(4), 10);
1120        assert_eq!(depth.get(), 5);
1121    }
1122
1123    #[test]
1124    fn test_conversions_between_variants() {
1125        let drops = Cell::new(0usize);
1126        struct Guard<'a>(&'a Cell<usize>);
1127        impl Drop for Guard<'_> {
1128            fn drop(&mut self) {
1129                self.0.set(self.0.get() + 1);
1130            }
1131        }
1132
1133        let g = Guard(&drops);
1134        let shared: InlineFn<'_, fn(i32) -> i32> = InlineFn::new(move |x| {
1135            let _ = &g;
1136            x * 2
1137        });
1138        assert_eq!(shared.call(5), 10);
1139
1140        let mut mutable = InlineFnMut::from(shared);
1141        assert_eq!(mutable.call(7), 14);
1142
1143        let once = InlineFnOnce::from(mutable);
1144        assert_eq!(once.call(9), 18);
1145        assert_eq!(drops.get(), 1);
1146
1147        let g2 = Guard(&drops);
1148        let shared2: InlineFn<'_, fn(i32) -> i32> = InlineFn::new(move |x| {
1149            let _ = &g2;
1150            x + 1
1151        });
1152        let once_direct = InlineFnOnce::from(shared2);
1153        assert_eq!(once_direct.call(4), 5);
1154        assert_eq!(drops.get(), 2);
1155    }
1156
1157    #[test]
1158    fn test_send_and_sync_variants() {
1159        fn assert_send<T: Send>(_: &T) {}
1160        fn assert_sync<T: Sync>(_: &T) {}
1161
1162        let counter = AtomicUsize::new(1);
1163        let f_sync = SendSyncInlineFn::<fn(usize) -> usize>::new(|v| {
1164            counter.fetch_add(v, Ordering::Relaxed)
1165        });
1166        assert_send(&f_sync);
1167        assert_sync(&f_sync);
1168        assert_eq!(f_sync.call(4), 1);
1169        assert_eq!(counter.load(Ordering::Relaxed), 5);
1170
1171        let f_send: SendInlineFn<'_, fn(usize) -> usize> = f_sync.into_send();
1172        assert_send(&f_send);
1173        assert_eq!(f_send.call(2), 5);
1174
1175        let f_local = f_send.into_local();
1176        assert_eq!(f_local.call(3), 7);
1177
1178        let f_send_explicit =
1179            SendInlineFn::<fn(usize) -> usize>::new_send(|v| counter.load(Ordering::Relaxed) + v);
1180        assert_send(&f_send_explicit);
1181        assert_eq!(f_send_explicit.call(2), 12);
1182
1183        let mut mut_state = 10u32;
1184        let mut f_mut_sync = SendSyncInlineFnMut::<fn(u32) -> u32>::new_send_sync(move |x| {
1185            mut_state += x;
1186            mut_state
1187        });
1188        assert_send(&f_mut_sync);
1189        assert_sync(&f_mut_sync);
1190        assert_eq!(f_mut_sync.call(5), 15);
1191
1192        let mut f_mut_send = f_mut_sync.into_send();
1193        assert_send(&f_mut_send);
1194        assert_eq!(f_mut_send.call(5), 20);
1195
1196        let mut f_mut_local = f_mut_send.into_local();
1197        assert_eq!(f_mut_local.call(5), 25);
1198
1199        let mut f_mut_send2 = SendInlineFnMut::<fn(u32) -> u32>::new_send(move |x| {
1200            mut_state += x;
1201            mut_state
1202        });
1203        assert_send(&f_mut_send2);
1204        assert_eq!(f_mut_send2.call(5), 15);
1205
1206        let mut f_mut_local2 = InlineFnMut::<fn(u32) -> u32>::new_local(move |x| {
1207            mut_state += x;
1208            mut_state
1209        });
1210        assert_eq!(f_mut_local2.call(3), 13);
1211
1212        let f_once_sync =
1213            SendSyncInlineFnOnce::<fn(u32) -> u32>::new_send_sync(move |x| mut_state + x);
1214        assert_send(&f_once_sync);
1215        assert_sync(&f_once_sync);
1216        let f_once_send = f_once_sync.into_send();
1217        assert_send(&f_once_send);
1218        let f_once_local = f_once_send.into_local();
1219        assert_eq!(f_once_local.call(20), 30);
1220
1221        let f_once_send2 = SendInlineFnOnce::<fn(u32) -> u32>::new_send(move |x| mut_state + x);
1222        assert_send(&f_once_send2);
1223        assert_eq!(f_once_send2.call(10), 20);
1224
1225        let f_once_local2 = InlineFnOnce::<fn(u32) -> u32>::new_local(move |x| mut_state + x);
1226        assert_eq!(f_once_local2.call(5), 15);
1227
1228        // Local variant can capture `!Send` / `!Sync` types like `Cell` or raw pointers.
1229        let local_cell = Cell::new(10u32);
1230        let raw_ptr: *const Cell<u32> = &local_cell;
1231        let local_fn: InlineFn<'_, fn(u32) -> u32> = InlineFn::new_local(move |x| {
1232            // SAFETY: `raw_ptr` points to `local_cell` on the same stack frame.
1233            let cell = unsafe { &*raw_ptr };
1234            cell.set(cell.get() + x);
1235            cell.get()
1236        });
1237        assert_eq!(local_fn.call(5), 15);
1238    }
1239
1240    #[test]
1241    fn test_debug_formatting() {
1242        struct Buf {
1243            bytes: [u8; 128],
1244            len: usize,
1245        }
1246        impl Buf {
1247            fn new() -> Self {
1248                Self { bytes: [0; 128], len: 0 }
1249            }
1250            fn as_str(&self) -> &str {
1251                core::str::from_utf8(&self.bytes[..self.len]).unwrap()
1252            }
1253        }
1254        impl fmt::Write for Buf {
1255            fn write_str(&mut self, s: &str) -> fmt::Result {
1256                let b = s.as_bytes();
1257                self.bytes[self.len..self.len + b.len()].copy_from_slice(b);
1258                self.len += b.len();
1259                Ok(())
1260            }
1261        }
1262
1263        let f: InlineFn<'_, fn()> = InlineFn::new(|| {});
1264        let mut buf = Buf::new();
1265        write!(&mut buf, "{:?}", f).unwrap();
1266        assert!(buf.as_str().starts_with("InlineFn { size: "));
1267
1268        let f_mut: InlineFnMut<'_, fn()> = InlineFnMut::new(|| {});
1269        let mut buf = Buf::new();
1270        write!(&mut buf, "{:?}", f_mut).unwrap();
1271        assert!(buf.as_str().starts_with("InlineFnMut { size: "));
1272
1273        let f_once: InlineFnOnce<'_, fn()> = InlineFnOnce::new(|| {});
1274        let mut buf = Buf::new();
1275        write!(&mut buf, "{:?}", f_once).unwrap();
1276        assert!(buf.as_str().starts_with("InlineFnOnce { size: "));
1277
1278        let mut buf = Buf::new();
1279        write!(
1280            &mut buf,
1281            "{:?},{:?},{:?}",
1282            Local(PhantomData),
1283            SendOnly(PhantomData),
1284            SendSync(PhantomData)
1285        )
1286        .unwrap();
1287        assert_eq!(buf.as_str(), "Local,SendOnly,SendSync");
1288    }
1289
1290    #[test]
1291    fn test_option_niche_optimization() {
1292        assert_eq!(
1293            core::mem::size_of::<Option<InlineFn<'static, fn()>>>(),
1294            core::mem::size_of::<InlineFn<'static, fn()>>()
1295        );
1296        assert_eq!(
1297            core::mem::size_of::<Option<InlineFnMut<'static, fn()>>>(),
1298            core::mem::size_of::<InlineFnMut<'static, fn()>>()
1299        );
1300        assert_eq!(
1301            core::mem::size_of::<Option<InlineFnOnce<'static, fn()>>>(),
1302            core::mem::size_of::<InlineFnOnce<'static, fn()>>()
1303        );
1304    }
1305}