xref: /qemu/rust/qemu-api/src/callbacks.rs (revision aa3a285b5bc56a4208b3b57d4a55291e9c260107)
1 // SPDX-License-Identifier: MIT
2 
3 //! Utility functions to deal with callbacks from C to Rust.
4 
5 use std::{mem, ptr::NonNull};
6 
7 /// Trait for functions (types implementing [`Fn`]) that can be used as
8 /// callbacks. These include both zero-capture closures and function pointers.
9 ///
10 /// In Rust, calling a function through the `Fn` trait normally requires a
11 /// `self` parameter, even though for zero-sized functions (including function
12 /// pointers) the type itself contains all necessary information to call the
13 /// function. This trait provides a `call` function that doesn't require `self`,
14 /// allowing zero-sized functions to be called using only their type.
15 ///
16 /// This enables zero-sized functions to be passed entirely through generic
17 /// parameters and resolved at compile-time. A typical use is a function
18 /// receiving an unused parameter of generic type `F` and calling it via
19 /// `F::call` or passing it to another function via `func::<F>`.
20 ///
21 /// QEMU uses this trick to create wrappers to C callbacks.  The wrappers
22 /// are needed to convert an opaque `*mut c_void` into a Rust reference,
23 /// but they only have a single opaque that they can use.  The `FnCall`
24 /// trait makes it possible to use that opaque for `self` or any other
25 /// reference:
26 ///
27 /// ```ignore
28 /// // The compiler creates a new `rust_bh_cb` wrapper for each function
29 /// // passed to `qemu_bh_schedule_oneshot` below.
30 /// unsafe extern "C" fn rust_bh_cb<T, F: for<'a> FnCall<(&'a T,)>>(
31 ///     opaque: *mut c_void,
32 /// ) {
33 ///     // SAFETY: the opaque was passed as a reference to `T`.
34 ///     F::call((unsafe { &*(opaque.cast::<T>()) }, ))
35 /// }
36 ///
37 /// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`.
38 /// // Using a reference allows usage in const context.
39 /// fn qemu_bh_schedule_oneshot<T, F: for<'a> FnCall<(&'a T,)>>(_f: &F, opaque: &T) {
40 ///     let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::<T, F>;
41 ///     unsafe {
42 ///         bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void)
43 ///     }
44 /// }
45 /// ```
46 ///
47 /// Each wrapper is a separate instance of `rust_bh_cb` and is therefore
48 /// compiled to a separate function ("monomorphization").  If you wanted
49 /// to pass `self` as the opaque value, the generic parameters would be
50 /// `rust_bh_cb::<Self, F>`.
51 ///
52 /// `Args` is a tuple type whose types are the arguments of the function,
53 /// while `R` is the returned type.
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// # use qemu_api::callbacks::FnCall;
59 /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
60 ///     F::call((s,))
61 /// }
62 ///
63 /// let s: String = call_it(&str::to_owned, "hello world");
64 /// assert_eq!(s, "hello world");
65 /// ```
66 ///
67 /// Note that the compiler will produce a different version of `call_it` for
68 /// each function that is passed to it.  Therefore the argument is not really
69 /// used, except to decide what is `F` and what `F::call` does.
70 ///
71 /// Attempting to pass a non-zero-sized closure causes a compile-time failure:
72 ///
73 /// ```compile_fail
74 /// # use qemu_api::callbacks::FnCall;
75 /// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String {
76 /// #     F::call((s,))
77 /// # }
78 /// let x: &'static str = "goodbye world";
79 /// call_it(&move |_| String::from(x), "hello workd");
80 /// ```
81 ///
82 /// # Safety
83 ///
84 /// Because `Self` is a zero-sized type, all instances of the type are
85 /// equivalent. However, in addition to this, `Self` must have no invariants
86 /// that could be violated by creating a reference to it.
87 ///
88 /// This is always true for zero-capture closures and function pointers, as long
89 /// as the code is able to name the function in the first place.
90 pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
91     /// Referring to this internal constant asserts that the `Self` type is
92     /// zero-sized.  Can be replaced by an inline const expression in
93     /// Rust 1.79.0+.
94     const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 0) };
95 
96     /// Call the function with the arguments in args.
97     fn call(a: Args) -> R;
98 }
99 
100 macro_rules! impl_call {
101     ($($args:ident,)* ) => (
102         // SAFETY: because each function is treated as a separate type,
103         // accessing `FnCall` is only possible in code that would be
104         // allowed to call the function.
105         unsafe impl<F, $($args,)* R> FnCall<($($args,)*), R> for F
106         where
107             F: 'static + Sync + Sized + Fn($($args, )*) -> R,
108         {
109             #[inline(always)]
110             fn call(a: ($($args,)*)) -> R {
111                 let _: () = Self::ASSERT_ZERO_SIZED;
112 
113                 // SAFETY: the safety of this method is the condition for implementing
114                 // `FnCall`.  As to the `NonNull` idiom to create a zero-sized type,
115                 // see https://github.com/rust-lang/libs-team/issues/292.
116                 let f: &'static F = unsafe { &*NonNull::<Self>::dangling().as_ptr() };
117                 let ($($args,)*) = a;
118                 f($($args,)*)
119             }
120         }
121     )
122 }
123 
124 impl_call!(_1, _2, _3, _4, _5,);
125 impl_call!(_1, _2, _3, _4,);
126 impl_call!(_1, _2, _3,);
127 impl_call!(_1, _2,);
128 impl_call!(_1,);
129 impl_call!();
130 
131 #[cfg(test)]
132 mod tests {
133     use super::*;
134 
135     // The `_f` parameter is unused but it helps the compiler infer `F`.
136     fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String {
137         F::call(("hello world",))
138     }
139 
140     #[test]
141     fn test_call() {
142         assert_eq!(do_test_call(&str::to_owned), "hello world")
143     }
144 }
145