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>
This commit is contained in:
Joel Dice
2022-07-12 17:47:58 -06:00
committed by GitHub
parent 56831e0a76
commit e31ff9dc67
5 changed files with 1042 additions and 139 deletions

View File

@@ -1,6 +1,7 @@
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::parse_macro_input;
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(
@@ -32,3 +33,46 @@ fn expand_variants(count: &syn::LitInt, mut ty: syn::ItemEnum) -> syn::Result<To
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)
}