xref: /qemu/rust/qemu-api/src/callbacks.rs (revision b4b0880c3aae98e5c08d4d785b0fe16f0b780081)
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 /// `()` can be used to indicate "no function":
83 ///
84 /// ```
85 /// # use qemu_api::callbacks::FnCall;
86 /// fn optional<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option<String> {
87 ///     if F::IS_SOME {
88 ///         Some(F::call((s,)))
89 ///     } else {
90 ///         None
91 ///     }
92 /// }
93 ///
94 /// assert!(optional(&(), "hello world").is_none());
95 /// ```
96 ///
97 /// Invoking `F::call` will then be a run-time error.
98 ///
99 /// ```should_panic
100 /// # use qemu_api::callbacks::FnCall;
101 /// # fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
102 /// #     F::call((s,))
103 /// # }
104 /// let s: String = call_it(&(), "hello world"); // panics
105 /// ```
106 ///
107 /// # Safety
108 ///
109 /// Because `Self` is a zero-sized type, all instances of the type are
110 /// equivalent. However, in addition to this, `Self` must have no invariants
111 /// that could be violated by creating a reference to it.
112 ///
113 /// This is always true for zero-capture closures and function pointers, as long
114 /// as the code is able to name the function in the first place.
115 pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
116     /// Referring to this internal constant asserts that the `Self` type is
117     /// zero-sized.  Can be replaced by an inline const expression in
118     /// Rust 1.79.0+.
119     const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 0) };
120 
121     /// Referring to this constant asserts that the `Self` type is an actual
122     /// function type, which can be used to catch incorrect use of `()`
123     /// at compile time.
124     ///
125     /// # Examples
126     ///
127     /// ```compile_fail
128     /// # use qemu_api::callbacks::FnCall;
129     /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
130     ///     let _: () = F::ASSERT_IS_SOME;
131     ///     F::call((s,))
132     /// }
133     ///
134     /// let s: String = call_it((), "hello world"); // does not compile
135     /// ```
136     ///
137     /// Note that this can be more simply `const { assert!(F::IS_SOME) }` in
138     /// Rust 1.79.0 or newer.
139     const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) };
140 
141     /// `true` if `Self` is an actual function type and not `()`.
142     ///
143     /// # Examples
144     ///
145     /// You can use `IS_SOME` to catch this at compile time:
146     ///
147     /// ```compile_fail
148     /// # use qemu_api::callbacks::FnCall;
149     /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
150     ///     const { assert!(F::IS_SOME) }
151     ///     F::call((s,))
152     /// }
153     ///
154     /// let s: String = call_it((), "hello world"); // does not compile
155     /// ```
156     const IS_SOME: bool;
157 
158     /// `false` if `Self` is an actual function type, `true` if it is `()`.
is_none() -> bool159     fn is_none() -> bool {
160         !Self::IS_SOME
161     }
162 
163     /// `true` if `Self` is an actual function type, `false` if it is `()`.
is_some() -> bool164     fn is_some() -> bool {
165         Self::IS_SOME
166     }
167 
168     /// Call the function with the arguments in args.
call(a: Args) -> R169     fn call(a: Args) -> R;
170 }
171 
172 /// `()` acts as a "null" callback.  Using `()` and `function` is nicer
173 /// than `None` and `Some(function)`, because the compiler is unable to
174 /// infer the type of just `None`.  Therefore, the trait itself acts as the
175 /// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`].
176 unsafe impl<Args, R> FnCall<Args, R> for () {
177     const IS_SOME: bool = false;
178 
179     /// Call the function with the arguments in args.
call(_a: Args) -> R180     fn call(_a: Args) -> R {
181         panic!("callback not specified")
182     }
183 }
184 
185 macro_rules! impl_call {
186     ($($args:ident,)* ) => (
187         // SAFETY: because each function is treated as a separate type,
188         // accessing `FnCall` is only possible in code that would be
189         // allowed to call the function.
190         unsafe impl<F, $($args,)* R> FnCall<($($args,)*), R> for F
191         where
192             F: 'static + Sync + Sized + Fn($($args, )*) -> R,
193         {
194             const IS_SOME: bool = true;
195 
196             #[inline(always)]
197             fn call(a: ($($args,)*)) -> R {
198                 let _: () = Self::ASSERT_ZERO_SIZED;
199 
200                 // SAFETY: the safety of this method is the condition for implementing
201                 // `FnCall`.  As to the `NonNull` idiom to create a zero-sized type,
202                 // see https://github.com/rust-lang/libs-team/issues/292.
203                 let f: &'static F = unsafe { &*NonNull::<Self>::dangling().as_ptr() };
204                 let ($($args,)*) = a;
205                 f($($args,)*)
206             }
207         }
208     )
209 }
210 
211 impl_call!(_1, _2, _3, _4, _5,);
212 impl_call!(_1, _2, _3, _4,);
213 impl_call!(_1, _2, _3,);
214 impl_call!(_1, _2,);
215 impl_call!(_1,);
216 impl_call!();
217 
218 #[cfg(test)]
219 mod tests {
220     use super::*;
221 
222     // The `_f` parameter is unused but it helps the compiler infer `F`.
do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String223     fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String {
224         F::call(("hello world",))
225     }
226 
227     #[test]
test_call()228     fn test_call() {
229         assert_eq!(do_test_call(&str::to_owned), "hello world")
230     }
231 
232     // The `_f` parameter is unused but it helps the compiler infer `F`.
do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F)233     fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) {
234         assert!(F::is_some());
235     }
236 
237     #[test]
test_is_some()238     fn test_is_some() {
239         do_test_is_some(&str::to_owned);
240     }
241 }
242