diff mbox series

[02/12] rust: add a utility module for compile-time type checks

Message ID 20241220142955.652636-3-pbonzini@redhat.com (mailing list archive)
State New
Headers show
Series Next round of qemu_api patches | expand

Commit Message

Paolo Bonzini Dec. 20, 2024, 2:29 p.m. UTC
It is relatively common in the low-level qemu_api code to assert that
a field of a struct has a specific type; for example, it can be used
to ensure that the fields match what the qemu_api and C code expects
for safety.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/meson.build       |  1 +
 rust/qemu-api/src/assertions.rs | 90 +++++++++++++++++++++++++++++++++
 rust/qemu-api/src/lib.rs        |  1 +
 3 files changed, 92 insertions(+)
 create mode 100644 rust/qemu-api/src/assertions.rs

Comments

Zhao Liu Dec. 25, 2024, 4:24 p.m. UTC | #1
On Fri, Dec 20, 2024 at 03:29:44PM +0100, Paolo Bonzini wrote:
> Date: Fri, 20 Dec 2024 15:29:44 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 02/12] rust: add a utility module for compile-time type
>  checks
> X-Mailer: git-send-email 2.47.1
> 
> It is relatively common in the low-level qemu_api code to assert that
> a field of a struct has a specific type; for example, it can be used
> to ensure that the fields match what the qemu_api and C code expects
> for safety.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/meson.build       |  1 +
>  rust/qemu-api/src/assertions.rs | 90 +++++++++++++++++++++++++++++++++
>  rust/qemu-api/src/lib.rs        |  1 +
>  3 files changed, 92 insertions(+)
>  create mode 100644 rust/qemu-api/src/assertions.rs
> 

Very useful! Previously I found qdev property macro lacks such type
check, but I falied to think of a good way to implement type_check in
Rust, and glad to see the correct approach! Besides qdev property, I
think vmstate also needs this.

And I think we can make the examples as the unit tests.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Paolo Bonzini Dec. 27, 2024, 4:26 a.m. UTC | #2
On 12/25/24 17:24, Zhao Liu wrote:
> On Fri, Dec 20, 2024 at 03:29:44PM +0100, Paolo Bonzini wrote:
>> Date: Fri, 20 Dec 2024 15:29:44 +0100
>> From: Paolo Bonzini <pbonzini@redhat.com>
>> Subject: [PATCH 02/12] rust: add a utility module for compile-time type
>>   checks
>> X-Mailer: git-send-email 2.47.1
>>
>> It is relatively common in the low-level qemu_api code to assert that
>> a field of a struct has a specific type; for example, it can be used
>> to ensure that the fields match what the qemu_api and C code expects
>> for safety.
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> ---
>>   rust/qemu-api/meson.build       |  1 +
>>   rust/qemu-api/src/assertions.rs | 90 +++++++++++++++++++++++++++++++++
>>   rust/qemu-api/src/lib.rs        |  1 +
>>   3 files changed, 92 insertions(+)
>>   create mode 100644 rust/qemu-api/src/assertions.rs
>>
> 
> Very useful! Previously I found qdev property macro lacks such type
> check, but I falied to think of a good way to implement type_check in
> Rust, and glad to see the correct approach! Besides qdev property, I
> think vmstate also needs this.

Right, though for vmstate (and probably qdev properties too) it's 
probably better to go from types to C structs 
(PropertyInfo/VMStateField) and avoid having to do the assertions.

> And I think we can make the examples as the unit tests.

Having doctests support in "make check" is on my list; I have started 
contributing a rust.doctest() function to Meson for that purpose.  For 
now they are limited to what can be run through Cargo (i.e. you cannot 
use libqemuutil.a functions) but we'll see if the function is accepted.

Paolo

> Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
> 
> 
>
diff mbox series

Patch

diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 9425ba7100c..60944a657de 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -15,6 +15,7 @@  _qemu_api_rs = static_library(
   structured_sources(
     [
       'src/lib.rs',
+      'src/assertions.rs',
       'src/bindings.rs',
       'src/bitops.rs',
       'src/callbacks.rs',
diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs
new file mode 100644
index 00000000000..1a6cb3a6c7c
--- /dev/null
+++ b/rust/qemu-api/src/assertions.rs
@@ -0,0 +1,90 @@ 
+// Copyright 2024, Red Hat Inc.
+// Author(s): Paolo Bonzini <pbonzini@redhat.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! This module provides macros to check the equality of types and
+//! the type of `struct` fields.  This can be useful to ensure that
+//! types match the expectations of C code.
+
+// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292
+// (stackoverflow answers are released under MIT license).
+
+#[doc(hidden)]
+pub trait EqType {
+    type Itself;
+}
+
+impl<T> EqType for T {
+    type Itself = T;
+}
+
+/// Assert that two types are the same.
+///
+/// # Examples
+///
+/// ```
+/// # use qemu_api::assert_same_type;
+/// # use std::ops::Deref;
+/// assert_same_type!(u32, u32);
+/// assert_same_type!(<Box<u32> as Deref>::Target, u32);
+/// ```
+///
+/// Different types will cause a compile failure
+///
+/// ```compile_fail
+/// # use qemu_api::assert_same_type;
+/// assert_same_type!(&Box<u32>, &u32);
+/// ```
+#[macro_export]
+macro_rules! assert_same_type {
+    ($t1:ty, $t2:ty) => {
+        const _: () = {
+            #[allow(unused)]
+            fn assert_same_type(v: $t1) {
+                fn types_must_be_equal<T, U>(_: T)
+                where
+                    T: $crate::assertions::EqType<Itself = U>,
+                {
+                }
+                types_must_be_equal::<_, $t2>(v);
+            }
+        };
+    };
+}
+
+/// Assert that a field of a struct has the given type.
+///
+/// # Examples
+///
+/// ```
+/// # use qemu_api::assert_field_type;
+/// pub struct A {
+///     field1: u32,
+/// }
+///
+/// assert_field_type!(A, field1, u32);
+/// ```
+///
+/// Different types will cause a compile failure
+///
+/// ```compile_fail
+/// # use qemu_api::assert_field_type;
+/// # pub struct A { field1: u32 }
+/// assert_field_type!(A, field1, i32);
+/// ```
+#[macro_export]
+macro_rules! assert_field_type {
+    ($t:ty, $i:ident, $ti:ty) => {
+        const _: () = {
+            #[allow(unused)]
+            fn assert_field_type(v: $t) {
+                fn types_must_be_equal<T, U>(_: T)
+                where
+                    T: $crate::assertions::EqType<Itself = U>,
+                {
+                }
+                types_must_be_equal::<_, $ti>(v.$i);
+            }
+        };
+    };
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 4b43e02c0f9..83c6a987c05 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -12,6 +12,7 @@ 
 #[rustfmt::skip]
 pub mod prelude;
 
+pub mod assertions;
 pub mod bitops;
 pub mod c_str;
 pub mod callbacks;