* implement wasmtime::component::flags! per #4308 This is the last macro needed to complete #4308. It supports generating a Rust type that represents a `flags` component type, analogous to how the [bitflags crate](https://crates.io/crates/bitflags) operates. Signed-off-by: Joel Dice <joel.dice@fermyon.com> * wrap `format_flags` output in parens This ensures we generate non-empty output even when no flags are set. Empty output for a `Debug` implementation would be confusing. Signed-off-by: Joel Dice <joel.dice@fermyon.com> * unconditionally derive `Lift` and `Lower` in wasmtime::component::flags! Per feedback on #4414, we now derive impls for those traits unconditionally, which simplifies the syntax of the macro. Also, I happened to notice an alignment bug in `LowerExpander::expand_variant`, so I fixed that and cleaned up some related code. Finally, I used @jameysharp's trick to calculate bit masks without looping. Signed-off-by: Joel Dice <joel.dice@fermyon.com> * fix shift overflow regression in previous commit Jamey pointed out my mistake: I didn't consider the case when the flag count was evenly divisible by the representation size. This fixes the problem and adds test cases to cover it. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
79 lines
2.1 KiB
Rust
79 lines
2.1 KiB
Rust
use proc_macro2::{Span, TokenStream};
|
|
use quote::{format_ident, quote};
|
|
use syn::parse::{Parse, ParseStream};
|
|
use syn::{parse_macro_input, Error, Result, Token};
|
|
|
|
#[proc_macro_attribute]
|
|
pub fn add_variants(
|
|
attr: proc_macro::TokenStream,
|
|
item: proc_macro::TokenStream,
|
|
) -> proc_macro::TokenStream {
|
|
expand_variants(
|
|
&parse_macro_input!(attr as syn::LitInt),
|
|
parse_macro_input!(item as syn::ItemEnum),
|
|
)
|
|
.unwrap_or_else(syn::Error::into_compile_error)
|
|
.into()
|
|
}
|
|
|
|
fn expand_variants(count: &syn::LitInt, mut ty: syn::ItemEnum) -> syn::Result<TokenStream> {
|
|
let count = count
|
|
.base10_digits()
|
|
.parse::<usize>()
|
|
.map_err(|_| syn::Error::new(count.span(), "expected unsigned integer"))?;
|
|
|
|
ty.variants = (0..count)
|
|
.map(|index| syn::Variant {
|
|
attrs: Vec::new(),
|
|
ident: syn::Ident::new(&format!("V{}", index), Span::call_site()),
|
|
fields: syn::Fields::Unit,
|
|
discriminant: None,
|
|
})
|
|
.collect();
|
|
|
|
Ok(quote!(#ty))
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct FlagsTest {
|
|
name: String,
|
|
flag_count: usize,
|
|
}
|
|
|
|
impl Parse for FlagsTest {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let name = input.parse::<syn::Ident>()?.to_string();
|
|
input.parse::<Token![,]>()?;
|
|
let flag_count = input.parse::<syn::LitInt>()?.base10_parse()?;
|
|
|
|
Ok(Self { name, flag_count })
|
|
}
|
|
}
|
|
|
|
#[proc_macro]
|
|
pub fn flags_test(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
expand_flags_test(&parse_macro_input!(input as FlagsTest))
|
|
.unwrap_or_else(Error::into_compile_error)
|
|
.into()
|
|
}
|
|
|
|
fn expand_flags_test(test: &FlagsTest) -> Result<TokenStream> {
|
|
let name = format_ident!("{}", test.name);
|
|
let flags = (0..test.flag_count)
|
|
.map(|index| {
|
|
let name = format_ident!("F{}", index);
|
|
quote!(const #name;)
|
|
})
|
|
.collect::<TokenStream>();
|
|
|
|
let expanded = quote! {
|
|
wasmtime::component::flags! {
|
|
#name {
|
|
#flags
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(expanded)
|
|
}
|