mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-01 23:03:54 -06:00
rust: qemu-api-macros: add automatic TryFrom/TryInto derivation
This is going to be fairly common. Using a custom procedural macro provides better error messages and automatically finds the right type. Note that this is different from the same-named macro in the derive_more crate. That one provides conversion from e.g. tuples to enums with tuple variants, not from integers to enums. Reviewed-by: Zhao Liu <zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
a3b620fff7
commit
809c703a60
2 changed files with 73 additions and 29 deletions
|
@ -45,7 +45,7 @@ pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary");
|
||||||
#[doc(alias = "offset")]
|
#[doc(alias = "offset")]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, qemu_api_macros::TryInto)]
|
||||||
pub enum RegisterOffset {
|
pub enum RegisterOffset {
|
||||||
/// Data Register
|
/// Data Register
|
||||||
///
|
///
|
||||||
|
@ -102,32 +102,6 @@ pub enum RegisterOffset {
|
||||||
//Reserved = 0x04C,
|
//Reserved = 0x04C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::convert::TryFrom<u64> for RegisterOffset {
|
|
||||||
type Error = u64;
|
|
||||||
|
|
||||||
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
|
||||||
macro_rules! case {
|
|
||||||
($($discriminant:ident),*$(,)*) => {
|
|
||||||
/* check that matching on all macro arguments compiles, which means we are not
|
|
||||||
* missing any enum value; if the type definition ever changes this will stop
|
|
||||||
* compiling.
|
|
||||||
*/
|
|
||||||
const fn _assert_exhaustive(val: RegisterOffset) {
|
|
||||||
match val {
|
|
||||||
$(RegisterOffset::$discriminant => (),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match value {
|
|
||||||
$(x if x == Self::$discriminant as u64 => Ok(Self::$discriminant),)*
|
|
||||||
_ => Err(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case! { DR, RSR, FR, FBRD, ILPR, IBRD, LCR_H, CR, FLS, IMSC, RIS, MIS, ICR, DMACR }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod registers {
|
pub mod registers {
|
||||||
//! Device registers exposed as typed structs which are backed by arbitrary
|
//! Device registers exposed as typed structs which are backed by arbitrary
|
||||||
//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
|
//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field,
|
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
|
||||||
Fields, Ident, Type, Visibility,
|
DeriveInput, Field, Fields, Ident, Meta, Path, Token, Type, Variant, Visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
@ -98,3 +98,73 @@ pub fn derive_offsets(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
TokenStream::from(expanded)
|
TokenStream::from(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> {
|
||||||
|
let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
|
||||||
|
if let Some(repr) = repr {
|
||||||
|
let nested = repr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
|
||||||
|
for meta in nested {
|
||||||
|
match meta {
|
||||||
|
Meta::Path(path) if path.is_ident("u8") => return Ok(path),
|
||||||
|
Meta::Path(path) if path.is_ident("u16") => return Ok(path),
|
||||||
|
Meta::Path(path) if path.is_ident("u32") => return Ok(path),
|
||||||
|
Meta::Path(path) if path.is_ident("u64") => return Ok(path),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(MacroError::Message(
|
||||||
|
format!("#[repr(u8/u16/u32/u64) required for {}", msg),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, MacroError> {
|
||||||
|
if let Data::Enum(e) = &input.data {
|
||||||
|
if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) {
|
||||||
|
return Err(MacroError::Message(
|
||||||
|
"Cannot derive TryInto for enum with non-unit variants.".to_string(),
|
||||||
|
v.fields.span(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(&e.variants)
|
||||||
|
} else {
|
||||||
|
Err(MacroError::Message(
|
||||||
|
"Cannot derive TryInto for union or struct.".to_string(),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip::macros(quote)]
|
||||||
|
fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
|
||||||
|
let repr = get_repr_uN(&input, "#[derive(TryInto)]")?;
|
||||||
|
|
||||||
|
let name = &input.ident;
|
||||||
|
let variants = get_variants(&input)?;
|
||||||
|
let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect();
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl core::convert::TryFrom<#repr> for #name {
|
||||||
|
type Error = #repr;
|
||||||
|
|
||||||
|
fn try_from(value: #repr) -> Result<Self, Self::Error> {
|
||||||
|
#(const #discriminants: #repr = #name::#discriminants as #repr;)*;
|
||||||
|
match value {
|
||||||
|
#(#discriminants => Ok(Self::#discriminants),)*
|
||||||
|
_ => Err(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(TryInto)]
|
||||||
|
pub fn derive_tryinto(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into);
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue