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

@@ -5,6 +5,7 @@ use crate::store::StoreOpaque;
use crate::{AsContext, AsContextMut, StoreContext, StoreContextMut, ValRaw};
use anyhow::{bail, Context, Result};
use std::borrow::Cow;
use std::fmt;
use std::marker;
use std::mem::{self, MaybeUninit};
use std::str;
@@ -1581,6 +1582,55 @@ pub fn typecheck_union(
}
}
/// Verify that the given wasm type is a flags type with the expected flags in the right order and with the right
/// names.
pub fn typecheck_flags(
ty: &InterfaceType,
types: &ComponentTypes,
expected: &[&str],
) -> Result<()> {
match ty {
InterfaceType::Flags(index) => {
let names = &types[*index].names;
if names.len() != expected.len() {
bail!(
"expected flags type with {} names, found {} names",
expected.len(),
names.len()
);
}
for (name, expected) in names.iter().zip(expected) {
if name != expected {
bail!("expected flag named {}, found {}", expected, name);
}
}
Ok(())
}
other => bail!("expected `flags` found `{}`", desc(other)),
}
}
/// Format the specified bitflags using the specified names for debugging
pub fn format_flags(bits: &[u32], names: &[&str], f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("(")?;
let mut wrote = false;
for (index, name) in names.iter().enumerate() {
if ((bits[index / 32] >> (index % 32)) & 1) != 0 {
if wrote {
f.write_str("|")?;
} else {
wrote = true;
}
f.write_str(name)?;
}
}
f.write_str(")")
}
unsafe impl<T> ComponentType for Option<T>
where
T: ComponentType,

View File

@@ -16,7 +16,7 @@ pub use self::func::{
};
pub use self::instance::{ExportInstance, Exports, Instance, InstancePre};
pub use self::linker::{Linker, LinkerInstance};
pub use wasmtime_component_macro::{ComponentType, Lift, Lower};
pub use wasmtime_component_macro::{flags, ComponentType, Lift, Lower};
// These items are expected to be used by an eventual
// `#[derive(ComponentType)]`, they are not part of Wasmtime's API stability
@@ -24,8 +24,8 @@ pub use wasmtime_component_macro::{ComponentType, Lift, Lower};
#[doc(hidden)]
pub mod __internal {
pub use super::func::{
align_to, next_field, typecheck_enum, typecheck_record, typecheck_union, typecheck_variant,
MaybeUninitExt, Memory, MemoryMut, Options,
align_to, format_flags, next_field, typecheck_enum, typecheck_flags, typecheck_record,
typecheck_union, typecheck_variant, MaybeUninitExt, Memory, MemoryMut, Options,
};
pub use crate::map_maybe_uninit;
pub use crate::store::StoreOpaque;