diff mbox series

[2/9] rust/vmstate: Support varray's num field wrapped in BqlCell

Message ID 20250414144943.1112885-3-zhao1.liu@intel.com (mailing list archive)
State New
Headers show
Series rust/hpet: Initial support for migration | expand

Commit Message

Zhao Liu April 14, 2025, 2:49 p.m. UTC
Currently, if the `num` field of a varray is not a numeric type, such as
being placed in a wrapper, the array variant of assert_field_type will
fail the check.

HPET currently wraps num_timers in BqlCell<>. Although BqlCell<> is not
necessary from strictly speaking, it makes sense for vmstate to respect
BqlCell.

The failure of assert_field_type is because it cannot convert BqlCell<T>
into usize for use as the index.

Therefore, first, implement `From` trait for common numeric types on
BqlCell<>. Then, abstract the wrapper and non-wrapper cases uniformly
into a `IntoUsize` trait and make assert_field_type to get usize type
index via `IntoUsize` trait.

Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
---
 rust/qemu-api/src/assertions.rs | 30 +++++++++++++++++++++++++++++-
 rust/qemu-api/src/cell.rs       | 23 +++++++++++++++++++++++
 2 files changed, 52 insertions(+), 1 deletion(-)

Comments

Paolo Bonzini April 15, 2025, 10:54 a.m. UTC | #1
On 4/14/25 16:49, Zhao Liu wrote:
> Currently, if the `num` field of a varray is not a numeric type, such as
> being placed in a wrapper, the array variant of assert_field_type will
> fail the check.
> 
> HPET currently wraps num_timers in BqlCell<>. Although BqlCell<> is not
> necessary from strictly speaking, it makes sense for vmstate to respect
> BqlCell.

Dropping BqlCell<> from num_timers is indeed possible.  But I agree that 
getting BqlCell<> varrays to work is a good thing anyway; then you can 
separately decide whether to drop BqlCell<> from num_timers.

> The failure of assert_field_type is because it cannot convert BqlCell<T>
> into usize for use as the index.
> 
> Therefore, first, implement `From` trait for common numeric types on
> BqlCell<>. Then, abstract the wrapper and non-wrapper cases uniformly
> into a `IntoUsize` trait and make assert_field_type to get usize type
> index via `IntoUsize` trait.
> 
> Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
> ---
>   rust/qemu-api/src/assertions.rs | 30 +++++++++++++++++++++++++++++-
>   rust/qemu-api/src/cell.rs       | 23 +++++++++++++++++++++++
>   2 files changed, 52 insertions(+), 1 deletion(-)

I think you can drop the "num=" case of assert_field_type!, and use 
something like this macro:

/// Drop everything up to the colon, with the intention that
/// `if_present!` is called inside an optional macro substitution
/// (such as `$(... $arg ...)?` or `$(... $arg ...)*`).  This allows
/// expanding `$result` depending on the presence of an argument,
/// even if the argument itself is not included in `$result`.
///
/// # Examples
///
/// ```
/// # use qemu_api::if_present;
/// macro_rules! is_present {
///     ($($cond:expr)?) => {
///         loop {
///             $(if_present!([$cond]: break true;);)?
///             #[allow(unreachable_code)]
///             break false;
///         }
///     }
/// }
///
/// assert!(!is_present!());
/// assert!(is_present!("abc"));
/// ```
#[macro_export]
macro_rules! if_present {
      ([$($cond:tt)*]: $($result:tt)*) => { $($result)* };
}

to expand the array part of the access:

assert_field_type!(...
     $($crate::if_present!([$num]: [0]))?;
);

With this change, assert_field_type! is nicer and at least the trait 
you're introducing in assertions.rs goes away...

> +// Orphan rules don't like something like `impl<T> From<BqlCell<T>> for T`.
> +// It's enough to just implement Into for common types.
> +macro_rules! impl_into_inner {
> +    ($type:ty) => {
> +        impl From<BqlCell<$type>> for $type {
> +            fn from(c: BqlCell<$type>) -> $type {
> +                c.get()
> +            }
> +        }
> +    };
> +}

... and it's not clear to me whether this is needed with the change 
above?  Would impl_vmstate_transparent!'s definition of VARRAY_FLAG be 
enough?

If not, I *think* you can do a blanket implementation of Into<T> for 
BqlCell<T>.  Maybe that's nicer, you can decide.

Paolo
diff mbox series

Patch

diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs
index eb12e9499a72..232cac5b8dba 100644
--- a/rust/qemu-api/src/assertions.rs
+++ b/rust/qemu-api/src/assertions.rs
@@ -22,6 +22,34 @@  impl<T> EqType for T {
     type Itself = T;
 }
 
+pub trait IntoUsize {
+    fn into_usize(v: Self) -> usize;
+}
+
+macro_rules! impl_into_usize {
+    ($type:ty) => {
+        impl IntoUsize for $type {
+            fn into_usize(v: Self) -> usize {
+                v.try_into().unwrap()
+            }
+        }
+
+        impl IntoUsize for crate::cell::BqlCell<$type> {
+            fn into_usize(v: Self) -> usize {
+                let tmp: $type = v.try_into().unwrap();
+                tmp.try_into().unwrap()
+            }
+        }
+    };
+}
+
+// vmstate_n_elems() in C side supports such types.
+impl_into_usize!(u8);
+impl_into_usize!(u16);
+impl_into_usize!(i32);
+impl_into_usize!(u32);
+impl_into_usize!(u64);
+
 /// Assert that two types are the same.
 ///
 /// # Examples
@@ -101,7 +129,7 @@  fn types_must_be_equal<T, U>(_: T)
                     T: $crate::assertions::EqType<Itself = U>,
                 {
                 }
-                let index: usize = v.$num.try_into().unwrap();
+                let index: usize = $crate::assertions::IntoUsize::into_usize(v.$num);
                 types_must_be_equal::<_, &$ti>(&v.$i[index]);
             }
         };
diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs
index ab0785a26928..d31bff093707 100644
--- a/rust/qemu-api/src/cell.rs
+++ b/rust/qemu-api/src/cell.rs
@@ -309,6 +309,29 @@  fn from(t: T) -> BqlCell<T> {
     }
 }
 
+// Orphan rules don't like something like `impl<T> From<BqlCell<T>> for T`.
+// It's enough to just implement Into for common types.
+macro_rules! impl_into_inner {
+    ($type:ty) => {
+        impl From<BqlCell<$type>> for $type {
+            fn from(c: BqlCell<$type>) -> $type {
+                c.get()
+            }
+        }
+    };
+}
+
+impl_into_inner!(bool);
+impl_into_inner!(i8);
+impl_into_inner!(i16);
+impl_into_inner!(i32);
+impl_into_inner!(i64);
+impl_into_inner!(u8);
+impl_into_inner!(u16);
+impl_into_inner!(u32);
+impl_into_inner!(u64);
+impl_into_inner!(usize);
+
 impl<T: fmt::Debug + Copy> fmt::Debug for BqlCell<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.get().fmt(f)