The existing translation of the C macros for vmstate does not make any attempt to type-check vmstate declarations against the struct, so introduce a new system that computes VMStateField based on the actual struct declaration.
Macros do not have full access to the type system, therefore a full implementation of this scheme requires a helper trait to analyze the type and produce a VMStateField from it; a macro "vmstate_of!" accepts arguments similar to "offset_of!" and tricks the compiler into looking up the trait for the right type. The patch introduces not just vmstate_of!, but also the slightly too clever enabling macro call_func_with_field!. The particular trick used here was proposed on the users.rust-lang.org forum, so I take no merit and all the blame. Introduce the trait and some functions to access it; the actual implementation comes later. Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- rust/qemu-api/src/prelude.rs | 2 + rust/qemu-api/src/vmstate.rs | 110 +++++++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 6 deletions(-) diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 4ea70b9c823..2dc86e19b29 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -18,3 +18,5 @@ pub use crate::qom_isa; pub use crate::sysbus::SysBusDeviceMethods; + +pub use crate::vmstate::VMState; diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 63c897abcdf..bfcf06e8f1d 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -4,13 +4,111 @@ //! Helper macros to declare migration state for device models. //! -//! Some macros are direct equivalents to the C macros declared in -//! `include/migration/vmstate.h` while -//! [`vmstate_subsections`](crate::vmstate_subsections) and -//! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when -//! declaring a device model state struct. +//! This module includes three families of macros: +//! +//! * [`vmstate_unused!`](crate::vmstate_unused) and +//! [`vmstate_of!`](crate::vmstate_of), which are used to express the +//! migration format for a struct. This is based on the [`VMState`] trait, +//! which is defined by all migrateable types. +//! +//! * helper macros to declare a device model state struct, in particular +//! [`vmstate_subsections`](crate::vmstate_subsections) and +//! [`vmstate_fields`](crate::vmstate_fields). +//! +//! * direct equivalents to the C macros declared in +//! `include/migration/vmstate.h`. These are not type-safe and should not be +//! used if the equivalent functionality is available with `vmstate_of!`. -pub use crate::bindings::VMStateDescription; +use core::marker::PhantomData; + +pub use crate::bindings::{VMStateDescription, VMStateField}; + +/// This macro is used to call a function with a generic argument bound +/// to the type of a field. The function must take a +/// [`PhantomData`]`<T>` argument; `T` is the type of +/// field `$field` in the `$typ` type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::call_func_with_field; +/// # use core::marker::PhantomData; +/// const fn size_of_field<T>(_: PhantomData<T>) -> usize { +/// std::mem::size_of::<T>() +/// } +/// +/// struct Foo { +/// x: u16, +/// }; +/// // calls size_of_field::<u16>() +/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2); +/// ``` +#[macro_export] +macro_rules! call_func_with_field { + ($func:expr, $typ:ty, $($field:tt).+) => { + $func(loop { + #![allow(unreachable_code)] + const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { ::core::marker::PhantomData } + // Unreachable code is exempt from checks on uninitialized values. + // Use that trick to infer the type of this PhantomData. + break ::core::marker::PhantomData; + break phantom__(&{ let value__: $typ; value__.$($field).+ }); + }) + }; +} + +/// A trait for types that can be included in a device's migration stream. It +/// provides the base contents of a `VMStateField` (minus the name and offset). +/// +/// # Safety +/// +/// The contents of this trait go straight into structs that are parsed by C +/// code and used to introspect into other structs. Be careful. +pub unsafe trait VMState { + /// The base contents of a `VMStateField` (minus the name and offset) for + /// the type that is implementing the trait. + const BASE: VMStateField; +} + +/// Internal utility function to retrieve a type's `VMStateField`; +/// used by [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_base<T: VMState>(_: PhantomData<T>) -> VMStateField { + T::BASE +} + +/// Return the `VMStateField` for a field of a struct. The field must be +/// visible in the current scope. +/// +/// In order to support other types, the trait `VMState` must be implemented +/// for them. +#[macro_export] +macro_rules! vmstate_of { + ($struct_name:ty, $field_name:ident $(,)?) => { + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + offset: $crate::offset_of!($struct_name, $field_name), + // Compute most of the VMStateField from the type of the field. + ..$crate::call_func_with_field!( + $crate::vmstate::vmstate_base, + $struct_name, + $field_name + ) + } + }; +} + +// Add a couple builder-style methods to VMStateField, allowing +// easy derivation of VMStateField constants from other types. +impl VMStateField { + #[must_use] + pub const fn with_version_id(mut self, version_id: i32) -> Self { + assert!(version_id >= 0); + self.version_id = version_id; + self + } +} #[doc(alias = "VMSTATE_UNUSED_BUFFER")] #[macro_export] -- 2.47.1