Skip to main content

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", target_arch = "x86_64");
250cc_impl!(Sysv64Unwind, "sysv64-unwind", target_arch = "x86_64");
251
252cc_impl!(Win64, "win64", target_arch = "x86_64");
253cc_impl!(Win64Unwind, "win64-unwind", target_arch = "x86_64");
254
255cc_impl!(Aapcs, "aapcs", target_arch = "arm");
256cc_impl!(AapcsUnwind, "aapcs-unwind", target_arch = "arm");
257
258cc_impl!(Fastcall, "fastcall", target_arch = "x86");
259cc_impl!(FastcallUnwind, "fastcall-unwind", target_arch = "x86");
260
261cc_impl!(Stdcall, "stdcall", target_arch = "x86");
262cc_impl!(StdcallUnwind, "stdcall-unwind", target_arch = "x86");
263
264cc_impl!(Cdecl, "cdecl", target_arch = "x86");
265cc_impl!(CdeclUnwind, "cdecl-unwind", target_arch = "x86");
266
267cc_impl!(Thiscall, "thiscall", target_arch = "x86");
268cc_impl!(ThiscallUnwind, "thiscall-unwind", target_arch = "x86");
269
270#[cfg(feature = "c_variadic")]
271macro_rules! cc_thunk_impl_triple_variadic {
272    (
273        $cconv:ty,
274        $cconv_lit:literal,
275        $extra_idx: tt,
276        ($($id_tys: ident,)*),
277        ($($tuple_idx: tt,)*),
278        ($($args:ident: $tys:ty,)*)
279    ) => {
280        #[doc(hidden)]
281        unsafe impl<R, $($id_tys),*> $crate::traits::FnPtr for unsafe extern $cconv_lit fn($($tys,)* ...) -> R {
282            type CC = $cconv;
283            type Args<'a, 'b, 'c> = ($($tys,)* core::ffi::VaList<'a>,);
284            type Ret<'a, 'b, 'c> = R;
285
286            #[allow(unused_variables)]
287            #[inline(always)]
288            unsafe fn call<'a, 'b, 'c>(self, args: Self::Args<'a, 'b, 'c>) -> Self::Ret<'a, 'b, 'c>
289            {
290                const {
291                    panic!("FnPtr::call is not supported on C variadics due to a language limitations")
292                }
293            }
294
295            #[inline(always)]
296            unsafe fn from_ptr(ptr: *const ()) -> Self {
297                unsafe { core::mem::transmute_copy(&ptr) }
298            }
299
300            #[inline(always)]
301            fn to_ptr(self) -> *const () {
302                self as *const _
303            }
304
305            #[inline(always)]
306            fn make_once_thunk<F>(fun: F) -> impl $crate::traits::FnOnceThunk<Self>
307            where
308                F: for<'a, 'b, 'c> $crate::traits::PackedFnOnce<'a, 'b, 'c, Self>
309            {
310                // needed to create a HRTB closure
311                #[inline(always)]
312                fn coerce<R, $($id_tys,)* F>(fun: F) -> F
313                where F: for<'va> FnOnce($($tys,)* core::ffi::VaList<'va>) -> R {
314                    fun
315                }
316                let coerced = coerce(move |$($args,)* va| fun(($($args,)* va,)));
317                (Self::CC::default(), coerced)
318            }
319
320            #[inline(always)]
321            fn make_mut_thunk<F>(mut fun: F) -> impl $crate::traits::FnMutThunk<Self>
322            where
323                F: for<'a, 'b, 'c> $crate::traits::PackedFnMut<'a, 'b, 'c, Self>
324            {
325                #[inline(always)]
326                fn coerce<R, $($id_tys,)* F>(fun: F) -> F
327                where F: for<'va> FnMut($($tys,)* core::ffi::VaList<'va>) -> R {
328                    fun
329                }
330                let coerced = coerce(move |$($args,)* va| fun(($($args,)* va,)));
331                (Self::CC::default(), coerced)
332            }
333
334            #[inline(always)]
335            fn make_thunk<F>(fun: F) -> impl $crate::traits::FnThunk<Self>
336            where
337                F: for<'a, 'b, 'c> $crate::traits::PackedFn<'a, 'b, 'c, Self>
338            {
339                #[inline(always)]
340                fn coerce<R, $($id_tys,)* F>(fun: F) -> F
341                where F: for<'va> Fn($($tys,)* core::ffi::VaList<'va>) -> R {
342                    fun
343                }
344                let coerced = coerce(move |$($args,)* va| fun(($($args,)* va,)));
345                (Self::CC::default(), coerced)
346            }
347        }
348
349        #[doc(hidden)]
350        unsafe impl<F: for<'va> FnOnce($($tys,)* core::ffi::VaList<'va>) -> R, R, $($id_tys),*>
351            $crate::traits::FnOnceThunk<unsafe extern $cconv_lit fn($($tys,)* ...) -> R> for ($cconv, F)
352        {
353            const THUNK_TEMPLATE_ONCE: *const u8 = {
354                #[cfg_attr(feature = "coverage", coverage(off))]
355                unsafe extern $cconv_lit fn thunk<F, R, $($id_tys),*>($($args: $tys,)* va_args: ...) -> R
356                where
357                    F: for<'va> FnOnce($($tys,)* core::ffi::VaList<'va>) -> R
358                {
359                    if const { core::mem::size_of::<F>() == 0 } {
360                        let fun: F = unsafe { core::mem::zeroed() };
361                        fun($($args,)* va_args)
362                    }
363                    else {
364                        let closure_ptr: *mut F;
365                        $crate::arch::_thunk_asm!(closure_ptr);
366                        $crate::arch::_invoke(|| closure_ptr.read()($($args,)* va_args))
367                    }
368                }
369                thunk::<F, R, $($tys),*> as *const u8
370            };
371
372            #[allow(unused_variables)]
373            #[inline(always)]
374            unsafe fn call_once<'a, 'b, 'c>(
375                self,
376                args: <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Args<'a, 'b, 'c>
377            ) ->
378                <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
379            {
380                (self.1)($(args.$tuple_idx,)* args.$extra_idx)
381            }
382        }
383
384        #[doc(hidden)]
385        unsafe impl<F: for<'va> FnMut($($tys,)* core::ffi::VaList<'va>) -> R, R, $($id_tys),*>
386            $crate::traits::FnMutThunk<unsafe extern $cconv_lit fn($($tys,)* ...) -> R> for ($cconv, F)
387        {
388            const THUNK_TEMPLATE_MUT: *const u8 = {
389                #[cfg_attr(feature = "coverage", coverage(off))]
390                unsafe extern $cconv_lit fn thunk<F, R, $($id_tys),*>($($args: $tys,)* va_args: ...) -> R
391                where
392                    F: for<'va> FnMut($($tys,)* core::ffi::VaList<'va>) -> R
393                {
394                    if const { core::mem::size_of::<F>() == 0 } {
395                        let fun: &mut F = unsafe { &mut *core::ptr::dangling_mut() };
396                        fun($($args,)* va_args)
397                    }
398                    else {
399                        let closure_ptr: *mut F;
400                        $crate::arch::_thunk_asm!(closure_ptr);
401                        $crate::arch::_invoke(|| (&mut *closure_ptr)($($args,)* va_args))
402                    }
403                }
404                thunk::<F, R, $($tys),*> as *const u8
405            };
406
407            #[allow(unused_variables)]
408            #[inline(always)]
409            unsafe fn call_mut<'a, 'b, 'c>(
410                &mut self,
411                args: <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Args<'a, 'b, 'c>
412            ) ->
413                <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
414            {
415                (self.1)($(args.$tuple_idx,)* args.$extra_idx)
416            }
417        }
418
419        #[doc(hidden)]
420        unsafe impl<F: for<'va> Fn($($tys,)* core::ffi::VaList<'va>) -> R, R, $($id_tys),*>
421            $crate::traits::FnThunk<unsafe extern $cconv_lit fn($($tys,)* ...) -> R> for ($cconv, F)
422        {
423            const THUNK_TEMPLATE: *const u8 = {
424                #[cfg_attr(feature = "coverage", coverage(off))]
425                unsafe extern $cconv_lit fn thunk<F, R, $($id_tys),*>($($args: $tys,)* va_args: ...) -> R
426                where
427                    F: for<'va> Fn($($tys,)* core::ffi::VaList<'va>) -> R
428                {
429                    if const { core::mem::size_of::<F>() == 0 } {
430                        let fun: &F = unsafe { &*core::ptr::dangling_mut() };
431                        fun($($args,)* va_args)
432                    }
433                    else {
434                        let closure_ptr: *const F;
435                        $crate::arch::_thunk_asm!(closure_ptr);
436                        $crate::arch::_invoke(|| (&*closure_ptr)($($args,)* va_args))
437                    }
438                }
439                thunk::<F, R, $($tys),*> as *const u8
440            };
441
442            #[allow(unused_variables)]
443            #[inline(always)]
444            unsafe fn call<'a, 'b, 'c>(
445                &self,
446                args: <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Args<'a, 'b, 'c>
447            ) ->
448                <unsafe extern $cconv_lit fn($($tys,)* ...) -> R as $crate::traits::FnPtr>::Ret<'a, 'b, 'c>
449            {
450                (self.1)($(args.$tuple_idx,)* args.$extra_idx)
451            }
452        }
453    };
454}
455
456/// Marker type representing the C variadic calling convention.
457///
458/// This is a separate marker type to enable richer type inference.
459#[derive(Debug, Clone, Copy, Default)]
460#[cfg(any(doc, feature = "c_variadic"))]
461pub struct Variadic;
462#[cfg(feature = "c_variadic")]
463cc_trait_impl!(Variadic, "C", cc_thunk_impl_triple_variadic);