aboutsummaryrefslogtreecommitdiff
path: root/rust/qemu-api/src/callbacks.rs
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-02-14 08:18:56 -0500
committerStefan Hajnoczi <stefanha@redhat.com>2025-02-14 08:18:56 -0500
commitb4b0880c3aae98e5c08d4d785b0fe16f0b780081 (patch)
treea6b916597ebcc01c6de58b0aa84a9cce4893a294 /rust/qemu-api/src/callbacks.rs
parentce315328f8e9bf5201db4217f3ffe0784110aa4b (diff)
parent4dafba778aa3e5f5fd3b2c6333afd7650dcf54e2 (diff)
downloadqemu-b4b0880c3aae98e5c08d4d785b0fe16f0b780081.zip
qemu-b4b0880c3aae98e5c08d4d785b0fe16f0b780081.tar.gz
qemu-b4b0880c3aae98e5c08d4d785b0fe16f0b780081.tar.bz2
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* rust: more qdev bindings * rust: HPET device model with timer and GPIO bindings * rust: small cleanups and fixes; run doctests during CI * ui/sdl2: reenable the SDL2 Windows keyboard hook procedure # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmet6qkUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroO4yQgAjSpJ8DChoEVrm6xgCUGPkC7VlI0A # 3WimcgiTUCUVqiywvLmObHRv9ld/b9mJ+2v/actDy39qioN3i3+RGpyeSRcysITd # 2AWQVOe6JuVfEyN+ihYq3yS3v1meDhzZbOzRNHgbTX20rMy/HWJFIvQbK4abQaVI # j8zaPYIjcfcH/ScEmmha88l6PJDMPy7fCEzQWx41oHKkQ8w4rhmarn9f3WcXB/SN # bCvm2NmkJFPsU/TCynWz7YSjrLWCsWjiDgxoDD1295QoeEvfcuD8Z6vPIA9BttGx # MUgcrXi4KnJI8W9gm5jAiKq+DSxFX6f7AwUDfb2l+Vrkq84s7bu9UVNQqA== # =/vpW # -----END PGP SIGNATURE----- # gpg: Signature made Thu 13 Feb 2025 07:50:49 EST # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (27 commits) ui/sdl2: reenable the SDL2 Windows keyboard hook procedure rust: fix doctests rust: vmstate: remove redundant link targets rust: qemu_api: add a documentation header for all modules i386: enable rust hpet for pc when rust is enabled rust/timer/hpet: add qom and qdev APIs support rust/timer/hpet: add basic HPET timer and HPETState rust/timer/hpet: define hpet_fw_cfg rust: add bindings for timer rust: add bindings for memattrs rust: add bindings for gpio_{in|out} initialization rust/irq: Add a helper to convert [InterruptSource] to pointer rust/qdev: add the macro to define bit property i386/fw_cfg: move hpet_cfg definition to hpet.c rust: pl011: convert pl011_create to safe Rust rust: chardev, qdev: add bindings to qdev_prop_set_chr rust: irq: define ObjectType for IRQState rust: bindings for MemoryRegionOps rust: bindings: add Send and Sync markers for types that have bindings rust: qdev: switch from legacy reset to Resettable ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'rust/qemu-api/src/callbacks.rs')
-rw-r--r--rust/qemu-api/src/callbacks.rs97
1 files changed, 97 insertions, 0 deletions
diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs
index 314f9dc..9642a16 100644
--- a/rust/qemu-api/src/callbacks.rs
+++ b/rust/qemu-api/src/callbacks.rs
@@ -79,6 +79,31 @@ use std::{mem, ptr::NonNull};
/// call_it(&move |_| String::from(x), "hello workd");
/// ```
///
+/// `()` can be used to indicate "no function":
+///
+/// ```
+/// # use qemu_api::callbacks::FnCall;
+/// fn optional<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option<String> {
+/// if F::IS_SOME {
+/// Some(F::call((s,)))
+/// } else {
+/// None
+/// }
+/// }
+///
+/// assert!(optional(&(), "hello world").is_none());
+/// ```
+///
+/// Invoking `F::call` will then be a run-time error.
+///
+/// ```should_panic
+/// # use qemu_api::callbacks::FnCall;
+/// # fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
+/// # F::call((s,))
+/// # }
+/// let s: String = call_it(&(), "hello world"); // panics
+/// ```
+///
/// # Safety
///
/// Because `Self` is a zero-sized type, all instances of the type are
@@ -93,10 +118,70 @@ pub unsafe trait FnCall<Args, R = ()>: 'static + Sync + Sized {
/// Rust 1.79.0+.
const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::<Self>() == 0) };
+ /// Referring to this constant asserts that the `Self` type is an actual
+ /// function type, which can be used to catch incorrect use of `()`
+ /// at compile time.
+ ///
+ /// # Examples
+ ///
+ /// ```compile_fail
+ /// # use qemu_api::callbacks::FnCall;
+ /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
+ /// let _: () = F::ASSERT_IS_SOME;
+ /// F::call((s,))
+ /// }
+ ///
+ /// let s: String = call_it((), "hello world"); // does not compile
+ /// ```
+ ///
+ /// Note that this can be more simply `const { assert!(F::IS_SOME) }` in
+ /// Rust 1.79.0 or newer.
+ const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) };
+
+ /// `true` if `Self` is an actual function type and not `()`.
+ ///
+ /// # Examples
+ ///
+ /// You can use `IS_SOME` to catch this at compile time:
+ ///
+ /// ```compile_fail
+ /// # use qemu_api::callbacks::FnCall;
+ /// fn call_it<F: for<'a> FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String {
+ /// const { assert!(F::IS_SOME) }
+ /// F::call((s,))
+ /// }
+ ///
+ /// let s: String = call_it((), "hello world"); // does not compile
+ /// ```
+ const IS_SOME: bool;
+
+ /// `false` if `Self` is an actual function type, `true` if it is `()`.
+ fn is_none() -> bool {
+ !Self::IS_SOME
+ }
+
+ /// `true` if `Self` is an actual function type, `false` if it is `()`.
+ fn is_some() -> bool {
+ Self::IS_SOME
+ }
+
/// Call the function with the arguments in args.
fn call(a: Args) -> R;
}
+/// `()` acts as a "null" callback. Using `()` and `function` is nicer
+/// than `None` and `Some(function)`, because the compiler is unable to
+/// infer the type of just `None`. Therefore, the trait itself acts as the
+/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`].
+unsafe impl<Args, R> FnCall<Args, R> for () {
+ const IS_SOME: bool = false;
+
+ /// Call the function with the arguments in args.
+ fn call(_a: Args) -> R {
+ panic!("callback not specified")
+ }
+}
+
macro_rules! impl_call {
($($args:ident,)* ) => (
// SAFETY: because each function is treated as a separate type,
@@ -106,6 +191,8 @@ macro_rules! impl_call {
where
F: 'static + Sync + Sized + Fn($($args, )*) -> R,
{
+ const IS_SOME: bool = true;
+
#[inline(always)]
fn call(a: ($($args,)*)) -> R {
let _: () = Self::ASSERT_ZERO_SIZED;
@@ -141,4 +228,14 @@ mod tests {
fn test_call() {
assert_eq!(do_test_call(&str::to_owned), "hello world")
}
+
+ // The `_f` parameter is unused but it helps the compiler infer `F`.
+ fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) {
+ assert!(F::is_some());
+ }
+
+ #[test]
+ fn test_is_some() {
+ do_test_is_some(&str::to_owned);
+ }
}