support variant, enum, and union derives (#4359)

* support variant, enum, and union derives

This is the second stage of implementing #4308.  It adds support for deriving
variant, enum, and union impls for `ComponentType`, `Lift`, and `Lower`.  It
also fixes derived record impls for generic `struct`s, which I had intended to
support in my previous commit, but forgot to test.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* deduplicate component-macro code

Thanks to @jameysharp for the suggestion!

Signed-off-by: Joel Dice <joel.dice@fermyon.com>
This commit is contained in:
Joel Dice
2022-06-30 17:18:28 -06:00
committed by GitHub
parent a2d49ebf27
commit f252ae34ec
4 changed files with 901 additions and 52 deletions

View File

@@ -1492,6 +1492,95 @@ pub fn typecheck_record(
}
}
/// Verify that the given wasm type is a variant with the expected cases in the right order and with the right
/// names.
pub fn typecheck_variant(
ty: &InterfaceType,
types: &ComponentTypes,
expected: &[(&str, fn(&InterfaceType, &ComponentTypes) -> Result<()>)],
) -> Result<()> {
match ty {
InterfaceType::Variant(index) => {
let cases = &types[*index].cases;
if cases.len() != expected.len() {
bail!(
"expected variant of {} cases, found {} cases",
expected.len(),
cases.len()
);
}
for (case, &(name, check)) in cases.iter().zip(expected) {
check(&case.ty, types)
.with_context(|| format!("type mismatch for case {}", name))?;
if case.name != name {
bail!("expected variant case named {}, found {}", name, case.name);
}
}
Ok(())
}
other => bail!("expected `variant` found `{}`", desc(other)),
}
}
/// Verify that the given wasm type is a enum with the expected cases in the right order and with the right
/// names.
pub fn typecheck_enum(ty: &InterfaceType, types: &ComponentTypes, expected: &[&str]) -> Result<()> {
match ty {
InterfaceType::Enum(index) => {
let names = &types[*index].names;
if names.len() != expected.len() {
bail!(
"expected enum of {} names, found {} names",
expected.len(),
names.len()
);
}
for (name, expected) in names.iter().zip(expected) {
if name != expected {
bail!("expected enum case named {}, found {}", expected, name);
}
}
Ok(())
}
other => bail!("expected `enum` found `{}`", desc(other)),
}
}
/// Verify that the given wasm type is a union with the expected cases in the right order.
pub fn typecheck_union(
ty: &InterfaceType,
types: &ComponentTypes,
expected: &[fn(&InterfaceType, &ComponentTypes) -> Result<()>],
) -> Result<()> {
match ty {
InterfaceType::Union(index) => {
let union_types = &types[*index].types;
if union_types.len() != expected.len() {
bail!(
"expected union of {} types, found {} types",
expected.len(),
union_types.len()
);
}
for (index, (ty, check)) in union_types.iter().zip(expected).enumerate() {
check(ty, types).with_context(|| format!("type mismatch for case {}", index))?;
}
Ok(())
}
other => bail!("expected `union` found `{}`", desc(other)),
}
}
unsafe impl<T> ComponentType for Option<T>
where
T: ComponentType,

View File

@@ -24,7 +24,8 @@ pub use wasmtime_component_macro::{ComponentType, Lift, Lower};
#[doc(hidden)]
pub mod __internal {
pub use super::func::{
align_to, next_field, typecheck_record, MaybeUninitExt, Memory, MemoryMut, Options,
align_to, next_field, typecheck_enum, typecheck_record, typecheck_union, typecheck_variant,
MaybeUninitExt, Memory, MemoryMut, Options,
};
pub use crate::map_maybe_uninit;
pub use crate::store::StoreOpaque;