closure_ffi/
cc.rs

1//! Defines calling convention marker types for use with `BareFn` variants.
2
3macro_rules! cc_thunk_impl_triple {
4    (
5        $cconv:ty,
6        $cconv_lit:literal,
7        $extra_idx: tt,
8        ($($id_tys: ident,)*),
9        ($($tuple_idx: tt,)*),
10        ($($args:ident: $tys:ty,)*)
11    ) => {
12        #[doc(hidden)]
13        unsafe impl<R, $($id_tys),*> $crate::traits::FnPtr for unsafe extern $cconv_lit fn($($tys,)*) -> R {
14            type CC = $cconv;
15            type Args<'a, 'b, 'c> = ($($tys,)*);
16            type Ret<'a, 'b, 'c> = R;
17
18            #[allow(unused_variables)]
19            #[inline(always)]
20            unsafe fn call<'a, 'b, 'c>(self, args: Self::Args<'a, 'b, 'c>) -> Self::Ret<'a, 'b, 'c>
21            {
22                unsafe { (self)($(args.$tuple_idx,)*) }
23            }
24
25            #[inline(always)]
26            unsafe fn from_ptr(ptr: *const ()) -> Self {
27                unsafe { core::mem::transmute_copy(&ptr) }
28            }
29
30            #[inline(always)]
31            fn to_ptr(self) -> *const () {
32                self as *const _
33            }
34
35            #[inline(always)]
36            fn make_once_thunk<F>(fun: F) -> impl $crate::traits::FnOnceThunk<Self>
37            where
38                F: for<'a, 'b, 'c> $crate::traits::PackedFnOnce<'a, 'b, 'c, Self>
39            {
40                (Self::CC::default(), move |$($args,)*| fun(($($args,)*)))
41            }
42
43            #[inline(always)]
44            fn make_mut_thunk<F>(mut fun: F) -> impl $crate::traits::FnMutThunk<Self>
45            where
46                F: for<'a, 'b, 'c> $crate::traits::PackedFnMut<'a, 'b, 'c, Self>
47            {
48                (Self::CC::default(), move |$($args,)*| fun(($($args,)*)))
49            }
50
51            #[inline(always)]
52            fn make_thunk<F>(fun: F) -> impl $crate::traits::FnThunk<Self>
53            where
54                F: for<'a, 'b, 'c> $crate::traits::PackedFn<'a, 'b, 'c, Self>
55            {
56                (Self::CC::default(), move |$($args,)*| fun(($($args,)*)))
57            }
58        }
59
60        #[doc(hidden)]
61        unsafe impl<F: FnOnce($($tys),*) -> R, R, $($id_tys),*>
62            $crate::traits::FnOnceThunk<unsafe extern $cconv_lit fn($($tys,)*) -> R> for ($cconv, F)
63        {
64            const THUNK_TEMPLATE_ONCE: *const u8 = {
65                #[cfg_attr(feature = "coverage", coverage(off))]
66                #[allow(clippy::too_many_arguments)]
67                unsafe extern $cconv_lit fn thunk<F: FnOnce($($tys),*) -> R, R, $($id_tys),*>($($args: $tys),*) -> R {
68                    if const { core::mem::size_of::<F>() == 0 } {
69                        let fun: F = unsafe { core::mem::zeroed() };
70                        fun($($args),*)
71                    }
72                    else {
73                        let closure_ptr: *mut F;
74                        $crate::arch::_thunk_asm!(closure_ptr);
75                        $crate::arch::_invoke(|| closure_ptr.read()($($args),*))
76                    }
77                }
78                thunk::<F, R, $($tys),*> as *const u8
79            };
80
81            #[allow(unused_variables)]
82            #[inline(always)]
83            unsafe fn call_once<'a, 'b, 'c>(self, args: ($($tys,)*)) ->
84                <unsafe extern $cconv_lit fn($($tys,)*) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
85            {
86                (self.1)($(args.$tuple_idx,)*)
87            }
88        }
89
90        #[doc(hidden)]
91        unsafe impl<F: FnMut($($tys),*) -> R, R, $($id_tys),*>
92            $crate::traits::FnMutThunk<unsafe extern $cconv_lit fn($($tys,)*) -> R> for ($cconv, F)
93        {
94            const THUNK_TEMPLATE_MUT: *const u8 = {
95                #[cfg_attr(feature = "coverage", coverage(off))]
96                #[allow(clippy::too_many_arguments)]
97                unsafe extern $cconv_lit fn thunk<F: FnMut($($tys),*) -> R, R, $($id_tys),*>($($args: $tys),*) -> R {
98                    if const { core::mem::size_of::<F>() == 0 } {
99                        let fun: &mut F = unsafe { &mut *core::ptr::dangling_mut() };
100                        fun($($args),*)
101                    }
102                    else {
103                        let closure_ptr: *mut F;
104                        $crate::arch::_thunk_asm!(closure_ptr);
105                        $crate::arch::_invoke(|| (&mut *closure_ptr)($($args),*))
106                    }
107                }
108                thunk::<F, R, $($tys),*> as *const u8
109            };
110
111            #[allow(unused_variables)]
112            #[inline(always)]
113            unsafe fn call_mut<'a, 'b, 'c>(&mut self, args: ($($tys,)*)) ->
114                <unsafe extern $cconv_lit fn($($tys,)*) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
115            {
116                (self.1)($(args.$tuple_idx,)*)
117            }
118
119        }
120
121        #[doc(hidden)]
122        unsafe impl<F: Fn($($tys),*) -> R, R, $($id_tys),*>
123            $crate::traits::FnThunk<unsafe extern $cconv_lit fn($($tys,)*) -> R> for ($cconv, F)
124        {
125            const THUNK_TEMPLATE: *const u8 = {
126                #[cfg_attr(feature = "coverage", coverage(off))]
127                #[allow(clippy::too_many_arguments)]
128                unsafe extern $cconv_lit fn thunk<F: Fn($($tys),*) -> R, R, $($id_tys),*>($($args: $tys),*) -> R {
129                    if const { core::mem::size_of::<F>() == 0 } {
130                        let fun: &F = unsafe { &*core::ptr::dangling_mut() };
131                        fun($($args),*)
132                    }
133                    else {
134                        let closure_ptr: *const F;
135                        $crate::arch::_thunk_asm!(closure_ptr);
136                        $crate::arch::_invoke(|| (&*closure_ptr)($($args),*))
137                    }
138                }
139                thunk::<F, R, $($tys),*> as *const u8
140            };
141
142            #[allow(unused_variables)]
143            #[inline(always)]
144            unsafe fn call<'a, 'b, 'c>(&self, args: ($($tys,)*)) ->
145                <unsafe extern $cconv_lit fn($($tys,)*) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
146            {
147                (self.1)($(args.$tuple_idx,)*)
148            }
149        }
150    };
151}
152
153macro_rules! cc_trait_impl_recursive {
154    // Case 1: Non-empty parameter lists
155    (
156        $cconv:ty,
157        $cconv_lit:literal,
158        $impl_macro:ident,
159        [$head_extra_idx:tt, $($tail_extra_idx:tt,)*],
160        [$head_id_ty:ident, $($tail_id_tys:ident,)*] ($($id_tys:ident,)*),
161        [$head_tuple_idx:tt, $($tail_tuple_idx:tt,)*] ($($tuple_idx:tt,)*),
162        [$head_arg:ident: $head_ty:ty, $($tail_args:ident: $tail_tys:ty,)*] ($($args:ident: $tys:ty,)*)
163    ) => {
164        $impl_macro!(
165            $cconv,
166            $cconv_lit,
167            $head_extra_idx,
168            ($($id_tys,)*),
169            ($($tuple_idx,)*),
170            ($($args: $tys,)*)
171        );
172
173        cc_trait_impl_recursive!(
174            $cconv,
175            $cconv_lit,
176            $impl_macro,
177            [$($tail_extra_idx,)*],
178            [$($tail_id_tys,)*] ($($id_tys,)* $head_id_ty,),
179            [$($tail_tuple_idx,)*] ($($tuple_idx,)* $head_tuple_idx,),
180            [$($tail_args: $tail_tys,)*] ($($args: $tys,)* $head_arg: $head_ty,)
181        );
182    };
183
184    // Case 2: Exhausted parameter lists
185    (
186        $cconv:ty,
187        $cconv_lit:literal,
188        $impl_macro:ident,
189        [$extra_idx:tt,],
190        [] ($($id_tys:ident,)*),
191        [] ($($tuple_idx:tt,)*),
192        [] ($($args:ident: $tys:ty,)*)
193    ) => {
194        $impl_macro!(
195            $cconv,
196            $cconv_lit,
197            $extra_idx,
198            ($($id_tys,)*),
199            ($($tuple_idx,)*),
200            ($($args: $tys,)*)
201        );
202    };
203}
204
205macro_rules! cc_trait_impl {
206    ($cconv:ty, $cconv_lit:literal, $impl_macro:ident) => {
207        cc_trait_impl_recursive!(
208            $cconv,
209            $cconv_lit,
210            $impl_macro,
211            [0,1,2,3,4,5,6,7,8,9,10,11,12,],
212            [T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,](),
213            [0,1,2,3,4,5,6,7,8,9,10,11,](),
214            [a0:T0,a1:T1,a2:T2,a3:T3,a4:T4,a5:T5,a6:T6,a7:T7,a8:T8,a9:T9,a10:T10,a11:T11,]()
215        );
216    };
217}
218
219macro_rules! cc_impl {
220    ($ty_name:tt, $lit_name:literal $(,$cfg:meta)?) => {
221        #[doc = "Marker type representing the"]
222        #[doc = $lit_name]
223        #[doc = "calling convention."]
224        #[derive(Debug, Clone, Copy, Default)]
225        $(#[cfg(any($cfg, doc))])?
226        pub struct $ty_name;
227        $(#[cfg($cfg)])?
228        cc_trait_impl!($ty_name, $lit_name, cc_thunk_impl_triple);
229    };
230}
231
232/// Marker type representing the Rust calling convention.
233///
234/// Note that since Rust has no stable ABI, it may change across compiler versions. Although
235/// unlikely, it is even allowed to change between compiler invocations. Do not rely on "Rust" bare
236/// functions from different binaries to be ABI compatible.
237#[derive(Debug, Clone, Copy, Default)]
238pub struct Rust;
239cc_trait_impl!(Rust, "Rust", cc_thunk_impl_triple);
240
241cc_impl!(C, "C");
242cc_impl!(CUnwind, "C-unwind");
243
244cc_impl!(System, "system");
245cc_impl!(SystemUnwind, "system-unwind");
246
247cc_impl!(Efiapi, "efiapi");
248
249cc_impl!(Sysv64, "sysv64", all(not(windows), target_arch = "x86_64"));
250cc_impl!(
251    Sysv64Unwind,
252    "sysv64-unwind",
253    all(not(windows), target_arch = "x86_64")
254);
255
256cc_impl!(Aapcs, "aapcs", target_arch = "arm");
257cc_impl!(AapcsUnwind, "aapcs-unwind", target_arch = "arm");
258
259cc_impl!(Fastcall, "fastcall", all(windows, target_arch = "x86"));
260cc_impl!(
261    FastcallUnwind,
262    "fastcall-unwind",
263    all(windows, target_arch = "x86")
264);
265
266cc_impl!(Stdcall, "stdcall", all(windows, target_arch = "x86"));
267cc_impl!(
268    StdcallUnwind,
269    "stdcall-unwind",
270    all(windows, target_arch = "x86")
271);
272
273cc_impl!(Cdecl, "cdecl", all(windows, target_arch = "x86"));
274cc_impl!(
275    CdeclUnwind,
276    "cdecl-unwind",
277    all(windows, target_arch = "x86")
278);
279
280cc_impl!(Thiscall, "thiscall", all(windows, target_arch = "x86"));
281cc_impl!(
282    ThiscallUnwind,
283    "thiscall-unwind",
284    all(windows, target_arch = "x86")
285);
286
287cc_impl!(Win64, "win64", all(windows, target_arch = "x86_64"));
288cc_impl!(
289    Win64Unwind,
290    "win64-unwind",
291    all(windows, target_arch = "x86_64")
292);
293
294#[cfg(feature = "c_variadic")]
295macro_rules! cc_thunk_impl_triple_variadic {
296    (
297        $cconv:ty,
298        $cconv_lit:literal,
299        $extra_idx: tt,
300        ($($id_tys: ident,)*),
301        ($($tuple_idx: tt,)*),
302        ($($args:ident: $tys:ty,)*)
303    ) => {
304        #[doc(hidden)]
305        unsafe impl<R, $($id_tys),*> $crate::traits::FnPtr for unsafe extern $cconv_lit fn($($tys,)* ...) -> R {
306            type CC = $cconv;
307            type Args<'a, 'b, 'c> = ($($tys,)* core::ffi::VaListImpl<'a>,);
308            type Ret<'a, 'b, 'c> = R;
309
310            #[allow(unused_variables)]
311            #[inline(always)]
312            unsafe fn call<'a, 'b, 'c>(self, args: Self::Args<'a, 'b, 'c>) -> Self::Ret<'a, 'b, 'c>
313            {
314                const {
315                    panic!("FnPtr::call is not supported on C variadics due to a language limitations")
316                }
317            }
318
319            #[inline(always)]
320            unsafe fn from_ptr(ptr: *const ()) -> Self {
321                unsafe { core::mem::transmute_copy(&ptr) }
322            }
323
324            #[inline(always)]
325            fn to_ptr(self) -> *const () {
326                self as *const _
327            }
328
329            #[inline(always)]
330            fn make_once_thunk<F>(fun: F) -> impl $crate::traits::FnOnceThunk<Self>
331            where
332                F: for<'a, 'b, 'c> $crate::traits::PackedFnOnce<'a, 'b, 'c, Self>
333            {
334                // needed to create a HRTB closure
335                #[inline(always)]
336                fn coerce<R, $($id_tys,)* F>(fun: F) -> F
337                where F: for<'va> FnOnce($($tys,)* core::ffi::VaListImpl<'va>) -> R {
338                    fun
339                }
340                let coerced = coerce(move |$($args,)* va| fun(($($args,)* va,)));
341                (Self::CC::default(), coerced)
342            }
343
344            #[inline(always)]
345            fn make_mut_thunk<F>(mut fun: F) -> impl $crate::traits::FnMutThunk<Self>
346            where
347                F: for<'a, 'b, 'c> $crate::traits::PackedFnMut<'a, 'b, 'c, Self>
348            {
349                #[inline(always)]
350                fn coerce<R, $($id_tys,)* F>(fun: F) -> F
351                where F: for<'va> FnMut($($tys,)* core::ffi::VaListImpl<'va>) -> R {
352                    fun
353                }
354                let coerced = coerce(move |$($args,)* va| fun(($($args,)* va,)));
355                (Self::CC::default(), coerced)
356            }
357
358            #[inline(always)]
359            fn make_thunk<F>(fun: F) -> impl $crate::traits::FnThunk<Self>
360            where
361                F: for<'a, 'b, 'c> $crate::traits::PackedFn<'a, 'b, 'c, Self>
362            {
363                #[inline(always)]
364                fn coerce<R, $($id_tys,)* F>(fun: F) -> F
365                where F: for<'va> Fn($($tys,)* core::ffi::VaListImpl<'va>) -> R {
366                    fun
367                }
368                let coerced = coerce(move |$($args,)* va| fun(($($args,)* va,)));
369                (Self::CC::default(), coerced)
370            }
371        }
372
373        #[doc(hidden)]
374        unsafe impl<F: for<'va> FnOnce($($tys,)* core::ffi::VaListImpl<'va>) -> R, R, $($id_tys),*>
375            $crate::traits::FnOnceThunk<unsafe extern $cconv_lit fn($($tys,)* ...) -> R> for ($cconv, F)
376        {
377            const THUNK_TEMPLATE_ONCE: *const u8 = {
378                #[cfg_attr(feature = "coverage", coverage(off))]
379                unsafe extern $cconv_lit fn thunk<F, R, $($id_tys),*>($($args: $tys,)* va_args: ...) -> R
380                where
381                    F: for<'va> FnOnce($($tys,)* core::ffi::VaListImpl<'va>) -> R
382                {
383                    if const { core::mem::size_of::<F>() == 0 } {
384                        let fun: F = unsafe { core::mem::zeroed() };
385                        fun($($args,)* va_args)
386                    }
387                    else {
388                        let closure_ptr: *mut F;
389                        $crate::arch::_thunk_asm!(closure_ptr);
390                        $crate::arch::_invoke(|| closure_ptr.read()($($args,)* va_args))
391                    }
392                }
393                thunk::<F, R, $($tys),*> as *const u8
394            };
395
396            #[allow(unused_variables)]
397            #[inline(always)]
398            unsafe fn call_once<'a, 'b, 'c>(
399                self,
400                args: <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Args<'a, 'b, 'c>
401            ) ->
402                <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
403            {
404                (self.1)($(args.$tuple_idx,)* args.$extra_idx)
405            }
406        }
407
408        #[doc(hidden)]
409        unsafe impl<F: for<'va> FnMut($($tys,)* core::ffi::VaListImpl<'va>) -> R, R, $($id_tys),*>
410            $crate::traits::FnMutThunk<unsafe extern $cconv_lit fn($($tys,)* ...) -> R> for ($cconv, F)
411        {
412            const THUNK_TEMPLATE_MUT: *const u8 = {
413                #[cfg_attr(feature = "coverage", coverage(off))]
414                unsafe extern $cconv_lit fn thunk<F, R, $($id_tys),*>($($args: $tys,)* va_args: ...) -> R
415                where
416                    F: for<'va> FnMut($($tys,)* core::ffi::VaListImpl<'va>) -> R
417                {
418                    if const { core::mem::size_of::<F>() == 0 } {
419                        let fun: &mut F = unsafe { &mut *core::ptr::dangling_mut() };
420                        fun($($args,)* va_args)
421                    }
422                    else {
423                        let closure_ptr: *mut F;
424                        $crate::arch::_thunk_asm!(closure_ptr);
425                        $crate::arch::_invoke(|| (&mut *closure_ptr)($($args,)* va_args))
426                    }
427                }
428                thunk::<F, R, $($tys),*> as *const u8
429            };
430
431            #[allow(unused_variables)]
432            #[inline(always)]
433            unsafe fn call_mut<'a, 'b, 'c>(
434                &mut self,
435                args: <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Args<'a, 'b, 'c>
436            ) ->
437                <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
438            {
439                (self.1)($(args.$tuple_idx,)* args.$extra_idx)
440            }
441        }
442
443        #[doc(hidden)]
444        unsafe impl<F: for<'va> Fn($($tys,)* core::ffi::VaListImpl<'va>) -> R, R, $($id_tys),*>
445            $crate::traits::FnThunk<unsafe extern $cconv_lit fn($($tys,)* ...) -> R> for ($cconv, F)
446        {
447            const THUNK_TEMPLATE: *const u8 = {
448                #[cfg_attr(feature = "coverage", coverage(off))]
449                unsafe extern $cconv_lit fn thunk<F, R, $($id_tys),*>($($args: $tys,)* va_args: ...) -> R
450                where
451                    F: for<'va> Fn($($tys,)* core::ffi::VaListImpl<'va>) -> R
452                {
453                    if const { core::mem::size_of::<F>() == 0 } {
454                        let fun: &F = unsafe { &*core::ptr::dangling_mut() };
455                        fun($($args,)* va_args)
456                    }
457                    else {
458                        let closure_ptr: *const F;
459                        $crate::arch::_thunk_asm!(closure_ptr);
460                        $crate::arch::_invoke(|| (&*closure_ptr)($($args,)* va_args))
461                    }
462                }
463                thunk::<F, R, $($tys),*> as *const u8
464            };
465
466            #[allow(unused_variables)]
467            #[inline(always)]
468            unsafe fn call<'a, 'b, 'c>(
469                &self,
470                args: <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Args<'a, 'b, 'c>
471            ) ->
472                <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
473            {
474                (self.1)($(args.$tuple_idx,)* args.$extra_idx)
475            }
476        }
477    };
478}
479
480/// Marker type representing the C variadic calling convention.
481///
482/// This is a separate marker type to enable richer type inference.
483#[derive(Debug, Clone, Copy, Default)]
484#[cfg(any(doc, feature = "c_variadic"))]
485pub struct Variadic;
486#[cfg(feature = "c_variadic")]
487cc_trait_impl!(Variadic, "C", cc_thunk_impl_triple_variadic);