Files
wasmtime/crates/misc/component-macro-test/src/lib.rs
Joel Dice e31ff9dc67 implement wasmtime::component::flags! per #4308 (#4414)
* 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>
2022-07-12 16:47:58 -07:00

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)
}