From d218845d35d2fe06f4824ae7e994d32800183f74 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 May 2022 10:07:25 -0500 Subject: [PATCH] Split out an `Enum` type from the `Variant` type (#211) * Split out an `Enum` type from the `Variant` type This was not 100% straightforward as the lowering of an enum wants to be `the_value as i32` in the Rust-to-wasm generator, but that's not working because sometimes `the_value` has type `&T` instead of `T` which can't be cast to `i32`. This means that lowerings in Rust are now always producing a `match` statement which should optimize to the same thing but won't be as easy on the eyes. Additionally a small change was made to the C code generator that `expected<_, _>` is now represented as a struct-of-union instead of special-cased to be an `enum`. The only reason it was special cased prior was that it was accidentally interpreted as an `enum` due to the `Variant::is_enum` check (which is now removed). * Move a test --- crates/gen-c/src/lib.rs | 163 +++---- crates/gen-core/src/lib.rs | 4 + crates/gen-js/src/lib.rs | 126 +++--- crates/gen-markdown/src/lib.rs | 28 +- crates/gen-rust-wasm/src/lib.rs | 56 ++- crates/gen-rust/src/lib.rs | 230 +++++----- crates/gen-spidermonkey/src/lib.rs | 17 +- crates/gen-wasmtime-py/src/lib.rs | 80 ++-- crates/gen-wasmtime/src/lib.rs | 39 +- crates/parser/src/abi.rs | 45 +- crates/parser/src/ast.rs | 20 +- crates/parser/src/ast/resolve.rs | 32 +- crates/parser/src/lib.rs | 31 +- crates/parser/src/sizealign.rs | 1 + crates/parser/tests/all.rs | 6 + .../{empty-variant2.wit => empty-enum.wit} | 0 .../tests/ui/parse-fail/empty-enum.wit.result | 5 + .../ui/parse-fail/empty-variant2.wit.result | 5 - crates/parser/tests/ui/types.wit.result | 34 +- crates/parser/tests/ui/wasi-clock.wit.result | 399 ++++-------------- crates/parser/tests/ui/wasi-http.wit.result | 12 +- crates/parser/tests/ui/wasi.wit.result | 399 ++++-------------- crates/test-helpers/src/lib.rs | 1 + crates/wasmlink/src/adapter/call.rs | 5 +- crates/wasmlink/src/module.rs | 1 + crates/wit-component/src/decoding.rs | 21 +- crates/wit-component/src/encoding.rs | 39 +- crates/wit-component/src/printing.rs | 28 +- tests/runtime/flavorful/wasm.c | 8 +- tests/runtime/variants/wasm.c | 2 +- 30 files changed, 817 insertions(+), 1020 deletions(-) rename crates/parser/tests/ui/parse-fail/{empty-variant2.wit => empty-enum.wit} (100%) create mode 100644 crates/parser/tests/ui/parse-fail/empty-enum.wit.result delete mode 100644 crates/parser/tests/ui/parse-fail/empty-variant2.wit.result diff --git a/crates/gen-c/src/lib.rs b/crates/gen-c/src/lib.rs index c7e1e025f..52bde1061 100644 --- a/crates/gen-c/src/lib.rs +++ b/crates/gen-c/src/lib.rs @@ -165,7 +165,8 @@ impl C { match ty { Type::Id(id) => match &iface.types[*id].kind { TypeDefKind::Type(t) => self.is_arg_by_pointer(iface, t), - TypeDefKind::Variant(v) => !v.is_enum(), + TypeDefKind::Variant(_) => true, + TypeDefKind::Enum(_) => false, TypeDefKind::Flags(_) => false, TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, }, @@ -244,6 +245,7 @@ impl C { TypeDefKind::Type(t) => self.print_ty(iface, t), TypeDefKind::Flags(_) | TypeDefKind::Tuple(_) + | TypeDefKind::Enum(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) | TypeDefKind::Variant(_) => { @@ -282,7 +284,9 @@ impl C { } match &ty.kind { TypeDefKind::Type(t) => self.print_ty_name(iface, t), - TypeDefKind::Record(_) | TypeDefKind::Flags(_) => unimplemented!(), + TypeDefKind::Record(_) | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => { + unimplemented!() + } TypeDefKind::Tuple(t) => { self.src.h("tuple"); self.src.h(&t.types.len().to_string()); @@ -324,7 +328,10 @@ impl C { self.src.h("typedef "); let kind = &iface.types[ty].kind; match kind { - TypeDefKind::Type(_) | TypeDefKind::Flags(_) | TypeDefKind::Record(_) => { + TypeDefKind::Type(_) + | TypeDefKind::Flags(_) + | TypeDefKind::Record(_) + | TypeDefKind::Enum(_) => { unreachable!() } TypeDefKind::Tuple(t) => { @@ -347,25 +354,21 @@ impl C { self.src.h(" val;\n"); self.src.h("}"); } else if let Some((ok, err)) = v.as_expected() { - if ok.is_none() && err.is_none() { - self.src.h("uint8_t"); - } else { - self.src.h("struct { - // 0 if `val` is `ok`, 1 otherwise - uint8_t tag; - union { - "); - if let Some(ok) = ok { - self.print_ty(iface, ok); - self.src.h(" ok;\n"); - } - if let Some(err) = err { - self.print_ty(iface, err); - self.src.h(" err;\n"); - } - self.src.h("} val;\n"); - self.src.h("}"); + self.src.h("struct { + // 0 if `val` is `ok`, 1 otherwise + uint8_t tag; + union { + "); + if let Some(ok) = ok { + self.print_ty(iface, ok); + self.src.h(" ok;\n"); + } + if let Some(err) = err { + self.print_ty(iface, err); + self.src.h(" err;\n"); } + self.src.h("} val;\n"); + self.src.h("}"); } else { unimplemented!(); } @@ -453,6 +456,7 @@ impl C { TypeDefKind::Type(t) => self.free(iface, t, "ptr"), TypeDefKind::Flags(_) => {} + TypeDefKind::Enum(_) => {} TypeDefKind::Record(r) => { for field in r.fields.iter() { @@ -527,6 +531,7 @@ impl C { TypeDefKind::Record(r) => r.fields.iter().any(|t| self.owns_anything(iface, &t.ty)), TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.owns_anything(iface, t)), TypeDefKind::Flags(_) => false, + TypeDefKind::Enum(_) => false, TypeDefKind::List(_) => true, TypeDefKind::Variant(v) => v .cases @@ -597,8 +602,8 @@ impl Return { // other records/lists/buffers always go to return pointers TypeDefKind::List(_) => self.retptrs.push(*orig_ty), - // Enums are scalars (this includes bools) - TypeDefKind::Variant(v) if v.is_enum() => { + // Enums are scalars + TypeDefKind::Enum(_) => { self.scalar = Some(Scalar::Type(*orig_ty)); } @@ -617,17 +622,15 @@ impl Return { // returned through the normal returns. if let Some((ok, err)) = r.as_expected() { if let Some(Type::Id(err)) = err { - if let TypeDefKind::Variant(e) = &iface.types[*err].kind { - if e.is_enum() { - self.scalar = Some(Scalar::ExpectedEnum { - err: *err, - max_err: e.cases.len(), - }); - if let Some(ok) = ok { - self.splat_tuples(iface, ok, ok); - } - return; + if let TypeDefKind::Enum(e) = &iface.types[*err].kind { + self.scalar = Some(Scalar::ExpectedEnum { + err: *err, + max_err: e.cases.len(), + }); + if let Some(ok) = ok { + self.splat_tuples(iface, ok, ok); } + return; } } } @@ -760,40 +763,31 @@ impl Generator for C { let prev = mem::take(&mut self.src.header); self.docs(docs); self.names.insert(&name.to_snake_case()).unwrap(); - if variant.is_enum() { - self.src.h("typedef "); - self.src.h(int_repr(variant.tag)); - self.src.h(" "); - self.print_namespace(iface); - self.src.h(&name.to_snake_case()); - self.src.h("_t;\n"); - } else { - self.src.h("typedef struct {\n"); - self.src.h(int_repr(variant.tag)); - self.src.h(" tag;\n"); - match variant.as_option() { - Some(ty) => { - self.print_ty(iface, ty); - self.src.h(" val;\n"); - } - None => { - self.src.h("union {\n"); - for case in variant.cases.iter() { - if let Some(ty) = &case.ty { - self.print_ty(iface, ty); - self.src.h(" "); - self.src.h(&case_field_name(case)); - self.src.h(";\n"); - } + self.src.h("typedef struct {\n"); + self.src.h(int_repr(variant.tag)); + self.src.h(" tag;\n"); + match variant.as_option() { + Some(ty) => { + self.print_ty(iface, ty); + self.src.h(" val;\n"); + } + None => { + self.src.h("union {\n"); + for case in variant.cases.iter() { + if let Some(ty) = &case.ty { + self.print_ty(iface, ty); + self.src.h(" "); + self.src.h(&case_field_name(case)); + self.src.h(";\n"); } - self.src.h("} val;\n"); } + self.src.h("} val;\n"); } - self.src.h("} "); - self.print_namespace(iface); - self.src.h(&name.to_snake_case()); - self.src.h("_t;\n"); } + self.src.h("} "); + self.print_namespace(iface); + self.src.h(&name.to_snake_case()); + self.src.h("_t;\n"); for (i, case) in variant.cases.iter().enumerate() { self.src.h(&format!( "#define {}_{}_{} {}\n", @@ -808,6 +802,30 @@ impl Generator for C { .insert(id, mem::replace(&mut self.src.header, prev)); } + fn type_enum(&mut self, iface: &Interface, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { + let prev = mem::take(&mut self.src.header); + self.docs(docs); + self.names.insert(&name.to_snake_case()).unwrap(); + self.src.h("typedef "); + self.src.h(int_repr(enum_.tag())); + self.src.h(" "); + self.print_namespace(iface); + self.src.h(&name.to_snake_case()); + self.src.h("_t;\n"); + for (i, case) in enum_.cases.iter().enumerate() { + self.src.h(&format!( + "#define {}_{}_{} {}\n", + iface.name.to_shouty_snake_case(), + name.to_shouty_snake_case(), + case.name.to_shouty_snake_case(), + i, + )); + } + + self.types + .insert(id, mem::replace(&mut self.src.header, prev)); + } + fn type_resource(&mut self, iface: &Interface, ty: ResourceId) { drop((iface, ty)); } @@ -1477,6 +1495,7 @@ impl Bindgen for FunctionBindgen<'_> { results.push(format!("*{}", name)); self.payloads.push(name); } + Instruction::VariantLower { variant, results: result_types, @@ -1491,11 +1510,6 @@ impl Bindgen for FunctionBindgen<'_> { .drain(self.payloads.len() - variant.cases.len()..) .collect::>(); - if results.len() == 1 && variant.is_enum() { - results.push(format!("(int32_t) {}", operands[0])); - return; - } - let mut variant_results = Vec::new(); for ty in result_types.iter() { let name = self.locals.tmp("variant"); @@ -1507,11 +1521,7 @@ impl Bindgen for FunctionBindgen<'_> { variant_results.push(name); } - let expr_to_match = if variant.is_enum() { - operands[0].to_string() - } else { - format!("({}).tag", operands[0]) - }; + let expr_to_match = format!("({}).tag", operands[0]); self.src .push_str(&format!("switch ((int32_t) {}) {{\n", expr_to_match)); @@ -1549,10 +1559,6 @@ impl Bindgen for FunctionBindgen<'_> { .drain(self.blocks.len() - variant.cases.len()..) .collect::>(); - if variant.is_enum() { - return results.push(operands.pop().unwrap()); - } - let ty = self.gen.type_string(iface, &Type::Id(*ty)); let result = self.locals.tmp("variant"); self.src.push_str(&format!("{} {};\n", ty, result)); @@ -1583,6 +1589,9 @@ impl Bindgen for FunctionBindgen<'_> { results.push(result); } + Instruction::EnumLower { .. } => results.push(format!("(int32_t) {}", operands[0])), + Instruction::EnumLift { .. } => results.push(operands.pop().unwrap()), + Instruction::ListCanonLower { .. } | Instruction::StringLower { .. } => { results.push(format!("(int32_t) ({}).ptr", operands[0])); results.push(format!("(int32_t) ({}).len", operands[0])); diff --git a/crates/gen-core/src/lib.rs b/crates/gen-core/src/lib.rs index 23adaf791..57eb5207c 100644 --- a/crates/gen-core/src/lib.rs +++ b/crates/gen-core/src/lib.rs @@ -65,6 +65,7 @@ pub trait Generator { variant: &Variant, docs: &Docs, ); + fn type_enum(&mut self, iface: &Interface, id: TypeId, name: &str, enum_: &Enum, docs: &Docs); fn type_resource(&mut self, iface: &Interface, ty: ResourceId); fn type_alias(&mut self, iface: &Interface, id: TypeId, name: &str, ty: &Type, docs: &Docs); fn type_list(&mut self, iface: &Interface, id: TypeId, name: &str, ty: &Type, docs: &Docs); @@ -92,6 +93,7 @@ pub trait Generator { TypeDefKind::Record(record) => self.type_record(iface, id, name, record, &ty.docs), TypeDefKind::Flags(flags) => self.type_flags(iface, id, name, flags, &ty.docs), TypeDefKind::Tuple(tuple) => self.type_tuple(iface, id, name, tuple, &ty.docs), + TypeDefKind::Enum(enum_) => self.type_enum(iface, id, name, enum_, &ty.docs), TypeDefKind::Variant(variant) => { self.type_variant(iface, id, name, variant, &ty.docs) } @@ -198,6 +200,7 @@ impl Types { } } TypeDefKind::Flags(_) => {} + TypeDefKind::Enum(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { if let Some(ty) = &case.ty { @@ -241,6 +244,7 @@ impl Types { } } TypeDefKind::Flags(_) => {} + TypeDefKind::Enum(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { if let Some(ty) = &case.ty { diff --git a/crates/gen-js/src/lib.rs b/crates/gen-js/src/lib.rs index c3783803c..c553f5724 100644 --- a/crates/gen-js/src/lib.rs +++ b/crates/gen-js/src/lib.rs @@ -172,6 +172,7 @@ impl Js { TypeDefKind::Tuple(t) => self.print_tuple(iface, t), TypeDefKind::Record(_) => panic!("anonymous record"), TypeDefKind::Flags(_) => panic!("anonymous flags"), + TypeDefKind::Enum(_) => panic!("anonymous enum"), TypeDefKind::Variant(v) => { if self.is_nullable_option(iface, v) { self.print_ty(iface, v.cases[1].ty.as_ref().unwrap()); @@ -404,26 +405,6 @@ impl Generator for Js { .ts(&format!("export type {} = ", name.to_camel_case())); self.print_ty(iface, variant.cases[1].ty.as_ref().unwrap()); self.src.ts("| null;\n"); - } else if variant.is_enum() { - self.src - .ts(&format!("export enum {} {{\n", name.to_camel_case())); - for (i, case) in variant.cases.iter().enumerate() { - self.docs(&case.docs); - let name = case.name.to_camel_case(); - self.src.ts(&format!("{} = {},\n", name, i)); - } - self.src.ts("}\n"); - - self.src.js(&format!( - "export const {} = Object.freeze({{\n", - name.to_camel_case() - )); - for (i, case) in variant.cases.iter().enumerate() { - let name = case.name.to_camel_case(); - self.src.js(&format!("{}: \"{}\",\n", i, name)); - self.src.js(&format!("\"{}\": {},\n", name, i)); - } - self.src.js("});\n"); } else { self.src .ts(&format!("export type {} = ", name.to_camel_case())); @@ -454,6 +435,36 @@ impl Generator for Js { } } + fn type_enum( + &mut self, + _iface: &Interface, + _id: TypeId, + name: &str, + enum_: &Enum, + docs: &Docs, + ) { + self.docs(docs); + self.src + .ts(&format!("export enum {} {{\n", name.to_camel_case())); + for (i, case) in enum_.cases.iter().enumerate() { + self.docs(&case.docs); + let name = case.name.to_camel_case(); + self.src.ts(&format!("{} = {},\n", name, i)); + } + self.src.ts("}\n"); + + self.src.js(&format!( + "export const {} = Object.freeze({{\n", + name.to_camel_case() + )); + for (i, case) in enum_.cases.iter().enumerate() { + let name = case.name.to_camel_case(); + self.src.js(&format!("{}: \"{}\",\n", i, name)); + self.src.js(&format!("\"{}\": {},\n", name, i)); + } + self.src.js("});\n"); + } + fn type_resource(&mut self, _iface: &Interface, ty: ResourceId) { if !self.in_import { self.exported_resources.insert(ty); @@ -1471,6 +1482,7 @@ impl Bindgen for FunctionBindgen<'_> { } Instruction::VariantPayloadName => results.push("e".to_string()), + Instruction::VariantLower { variant, results: result_types, @@ -1485,29 +1497,12 @@ impl Bindgen for FunctionBindgen<'_> { self.src .js(&format!("const variant{} = {};\n", tmp, operands[0])); - if result_types.len() == 1 && variant.is_enum() && name.is_some() { - let name = name.unwrap().to_camel_case(); - self.src - .js(&format!("if (!(variant{} in {}))\n", tmp, name)); - self.src.js(&format!( - "throw new RangeError(\"invalid variant specified for {}\");\n", - name, - )); - results.push(format!( - "Number.isInteger(variant{}) ? variant{0} : {}[variant{0}]", - tmp, name - )); - return; - } - for i in 0..result_types.len() { self.src.js(&format!("let variant{}_{};\n", tmp, i)); results.push(format!("variant{}_{}", tmp, i)); } - let expr_to_match = if self.gen.is_nullable_option(iface, variant) - || (variant.is_enum() && name.is_some()) - { + let expr_to_match = if self.gen.is_nullable_option(iface, variant) { format!("variant{}", tmp) } else { format!("variant{}.tag", tmp) @@ -1515,9 +1510,7 @@ impl Bindgen for FunctionBindgen<'_> { self.src.js(&format!("switch ({}) {{\n", expr_to_match)); let mut use_default = true; - for (i, (case, (block, block_results))) in - variant.cases.iter().zip(blocks).enumerate() - { + for (case, (block, block_results)) in variant.cases.iter().zip(blocks) { if self.gen.is_nullable_option(iface, variant) { if case.ty.is_none() { self.src.js("case null: {\n"); @@ -1526,9 +1519,6 @@ impl Bindgen for FunctionBindgen<'_> { self.src.js(&format!("const e = variant{};\n", tmp)); use_default = false; } - } else if variant.is_enum() && name.is_some() { - self.src.js(&format!("case {}: {{\n", i)); - self.src.js(&format!("const e = variant{};\n", tmp)); } else { self.src .js(&format!("case \"{}\": {{\n", case.name.as_str())); @@ -1571,18 +1561,6 @@ impl Bindgen for FunctionBindgen<'_> { .collect::>(); let tmp = self.tmp(); - if variant.is_enum() && name.is_some() { - let name = name.unwrap().to_camel_case(); - self.src - .js(&format!("const tag{} = {};\n", tmp, operands[0])); - self.src.js(&format!("if (!(tag{} in {}))\n", tmp, name)); - self.src.js(&format!( - "throw new RangeError(\"invalid discriminant specified for {}\");\n", - name, - )); - results.push(format!("tag{}", tmp)); - return; - } self.src.js(&format!("let variant{};\n", tmp)); self.src.js(&format!("switch ({}) {{\n", operands[0])); @@ -1592,10 +1570,7 @@ impl Bindgen for FunctionBindgen<'_> { self.src.js(&format!("case {}: {{\n", i)); self.src.js(&block); - if variant.is_enum() && name.is_some() { - assert!(block_results.is_empty()); - self.src.js(&format!("variant{} = tag{0};\n", tmp)); - } else if self.gen.is_nullable_option(iface, variant) { + if self.gen.is_nullable_option(iface, variant) { if case.ty.is_none() { assert!(block_results.is_empty()); self.src.js(&format!("variant{} = null;\n", tmp)); @@ -1636,6 +1611,37 @@ impl Bindgen for FunctionBindgen<'_> { results.push(format!("variant{}", tmp)); } + Instruction::EnumLower { name, .. } => { + let tmp = self.tmp(); + self.src + .js(&format!("const variant{} = {};\n", tmp, operands[0])); + + let name = name.to_camel_case(); + self.src + .js(&format!("if (!(variant{} in {}))\n", tmp, name)); + self.src.js(&format!( + "throw new RangeError(\"invalid variant specified for {}\");\n", + name, + )); + results.push(format!( + "Number.isInteger(variant{}) ? variant{0} : {}[variant{0}]", + tmp, name + )); + } + + Instruction::EnumLift { name, .. } => { + let tmp = self.tmp(); + let name = name.to_camel_case(); + self.src + .js(&format!("const tag{} = {};\n", tmp, operands[0])); + self.src.js(&format!("if (!(tag{} in {}))\n", tmp, name)); + self.src.js(&format!( + "throw new RangeError(\"invalid discriminant specified for {}\");\n", + name, + )); + results.push(format!("tag{}", tmp)); + } + Instruction::ListCanonLower { element, realloc } => { // Lowering only happens when we're passing lists into wasm, // which forces us to always allocate, so this should always be diff --git a/crates/gen-markdown/src/lib.rs b/crates/gen-markdown/src/lib.rs index f2aefbb5c..00c75f617 100644 --- a/crates/gen-markdown/src/lib.rs +++ b/crates/gen-markdown/src/lib.rs @@ -78,7 +78,9 @@ impl Markdown { } self.src.push_str(")"); } - TypeDefKind::Record(_) | TypeDefKind::Flags(_) => unreachable!(), + TypeDefKind::Record(_) | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => { + unreachable!() + } TypeDefKind::Variant(v) => { if let Some(t) = v.as_option() { self.src.push_str("option<"); @@ -277,6 +279,30 @@ impl Generator for Markdown { } } + fn type_enum(&mut self, _iface: &Interface, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { + self.print_type_header(name); + self.src.push_str("enum\n\n"); + self.print_type_info(id, docs); + self.src.push_str("\n### Enum Cases\n\n"); + for case in enum_.cases.iter() { + self.src.push_str(&format!( + "- [`{name}`](#{v}.{c})", + v = name.to_snake_case(), + c = case.name.to_snake_case(), + name = case.name, + )); + self.hrefs.insert( + format!("{}::{}", name, case.name), + format!("#{}.{}", name.to_snake_case(), case.name.to_snake_case()), + ); + self.src.indent(1); + self.src.push_str("\n\n"); + self.docs(&case.docs); + self.src.deindent(1); + self.src.push_str("\n"); + } + } + fn type_resource(&mut self, iface: &Interface, ty: ResourceId) { drop((iface, ty)); } diff --git a/crates/gen-rust-wasm/src/lib.rs b/crates/gen-rust-wasm/src/lib.rs index 85db18bf9..c90bcfe04 100644 --- a/crates/gen-rust-wasm/src/lib.rs +++ b/crates/gen-rust-wasm/src/lib.rs @@ -229,11 +229,22 @@ impl Generator for RustWasm { &mut self, iface: &Interface, id: TypeId, - name: &str, + _name: &str, variant: &Variant, docs: &Docs, ) { - self.print_typedef_variant(iface, id, name, variant, docs); + self.print_typedef_variant(iface, id, variant, docs); + } + + fn type_enum( + &mut self, + _iface: &Interface, + _id: TypeId, + name: &str, + enum_: &Enum, + docs: &Docs, + ) { + self.print_typedef_enum(name, enum_, docs); } fn type_resource(&mut self, iface: &Interface, ty: ResourceId) { @@ -1028,6 +1039,47 @@ impl Bindgen for FunctionBindgen<'_> { results.push(result); } + Instruction::EnumLower { enum_, name, .. } => { + let mut result = format!("match {} {{\n", operands[0]); + let name = name.to_camel_case(); + for (i, case) in enum_.cases.iter().enumerate() { + let case = wit_bindgen_gen_rust::case_name(&case.name); + result.push_str(&format!("{name}::{case} => {i},\n")); + } + result.push_str("}"); + results.push(result); + } + + // In unchecked mode when this type is a named enum then we know we + // defined the type so we can transmute directly into it. + Instruction::EnumLift { enum_, name, .. } if unchecked => { + let mut result = format!("core::mem::transmute::<_, "); + result.push_str(&name.to_camel_case()); + result.push_str(">("); + result.push_str(&operands[0]); + result.push_str(" as "); + result.push_str(int_repr(enum_.tag())); + result.push_str(")"); + results.push(result); + } + + Instruction::EnumLift { enum_, name, .. } => { + let mut result = format!("match "); + result.push_str(&operands[0]); + result.push_str(" {\n"); + for (i, case) in enum_.cases.iter().enumerate() { + result.push_str(&i.to_string()); + result.push_str(" => "); + result.push_str(&name.to_camel_case()); + result.push_str("::"); + result.push_str(&wit_bindgen_gen_rust::case_name(&case.name)); + result.push_str(",\n"); + } + result.push_str("_ => panic!(\"invalid enum discriminant\"),\n"); + result.push_str("}"); + results.push(result); + } + Instruction::ListCanonLower { realloc, .. } => { let tmp = self.tmp(); let val = format!("vec{}", tmp); diff --git a/crates/gen-rust/src/lib.rs b/crates/gen-rust/src/lib.rs index 02da500a0..0880d21ce 100644 --- a/crates/gen-rust/src/lib.rs +++ b/crates/gen-rust/src/lib.rs @@ -229,6 +229,7 @@ pub trait RustGenerator { | TypeDefKind::Record(_) | TypeDefKind::List(_) | TypeDefKind::Flags(_) + | TypeDefKind::Enum(_) | TypeDefKind::Tuple(_) => true, TypeDefKind::Type(Type::Id(t)) => needs_generics(iface, &iface.types[*t].kind), TypeDefKind::Type(Type::String) => true, @@ -284,6 +285,9 @@ pub trait RustGenerator { TypeDefKind::Flags(_) => { panic!("unsupported anonymous type reference: flags") } + TypeDefKind::Enum(_) => { + panic!("unsupported anonymous type reference: enum") + } TypeDefKind::Type(t) => self.print_ty(iface, t, mode), } @@ -446,12 +450,9 @@ pub trait RustGenerator { &mut self, iface: &Interface, id: TypeId, - name: &str, variant: &Variant, docs: &Docs, ) { - // TODO: should this perhaps be an attribute in the wit file? - let is_error = name.contains("errno") && variant.is_enum(); let info = self.info(id); for (name, mode) in self.modes_of(iface, id) { @@ -480,11 +481,7 @@ pub trait RustGenerator { self.push_str(">;\n"); continue; } - if variant.is_enum() { - self.push_str("#[repr("); - self.int_repr(variant.tag); - self.push_str(")]\n#[derive(Clone, Copy, PartialEq, Eq)]\n"); - } else if !info.owns_data() { + if !info.owns_data() { self.push_str("#[derive(Clone, Copy)]\n"); } else if !info.has_handle { self.push_str("#[derive(Clone)]\n"); @@ -504,104 +501,138 @@ pub trait RustGenerator { } self.push_str("}\n"); - // Auto-synthesize an implementation of the standard `Error` trait for - // error-looking types based on their name. - if is_error { - self.push_str("impl "); + self.push_str("impl"); + self.print_generics(&info, lt, true); + self.push_str(" std::fmt::Debug for "); + self.push_str(&name); + self.print_generics(&info, lt, false); + self.push_str(" {\n"); + self.push_str("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n"); + self.push_str("match self {\n"); + for case in variant.cases.iter() { self.push_str(&name); - self.push_str("{\n"); - - self.push_str("pub fn name(&self) -> &'static str {\n"); - self.push_str("match self {\n"); - for case in variant.cases.iter() { - self.push_str(&name); - self.push_str("::"); - self.push_str(&case_name(&case.name)); - self.push_str(" => \""); - self.push_str(case.name.as_str()); - self.push_str("\",\n"); + self.push_str("::"); + self.push_str(&case_name(&case.name)); + if case.ty.is_some() { + self.push_str("(e)"); } - self.push_str("}\n"); - self.push_str("}\n"); - - self.push_str("pub fn message(&self) -> &'static str {\n"); - self.push_str("match self {\n"); - for case in variant.cases.iter() { - self.push_str(&name); - self.push_str("::"); - self.push_str(&case_name(&case.name)); - self.push_str(" => \""); - if let Some(contents) = &case.docs.contents { - self.push_str(contents.trim()); - } - self.push_str("\",\n"); + self.push_str(" => {\n"); + self.push_str(&format!( + "f.debug_tuple(\"{}::{}\")", + name, + case_name(&case.name) + )); + if case.ty.is_some() { + self.push_str(".field(e)"); } + self.push_str(".finish()\n"); self.push_str("}\n"); - self.push_str("}\n"); + } + self.push_str("}\n"); + self.push_str("}\n"); + self.push_str("}\n"); + } + } - self.push_str("}\n"); + fn print_typedef_enum(&mut self, name: &str, enum_: &Enum, docs: &Docs) { + // TODO: should this perhaps be an attribute in the wit file? + let is_error = name.contains("errno"); + + let name = name.to_camel_case(); + self.rustdoc(docs); + self.push_str("#[repr("); + self.int_repr(enum_.tag()); + self.push_str(")]\n#[derive(Clone, Copy, PartialEq, Eq)]\n"); + self.push_str(&format!("pub enum {} {{\n", name.to_camel_case())); + for case in enum_.cases.iter() { + self.rustdoc(&case.docs); + self.push_str(&case_name(&case.name)); + self.push_str(",\n"); + } + self.push_str("}\n"); + + // Auto-synthesize an implementation of the standard `Error` trait for + // error-looking types based on their name. + if is_error { + self.push_str("impl "); + self.push_str(&name); + self.push_str("{\n"); - self.push_str("impl std::fmt::Debug for "); - self.push_str(&name); - self.push_str( - "{\nfn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n", - ); - self.push_str("f.debug_struct(\""); + self.push_str("pub fn name(&self) -> &'static str {\n"); + self.push_str("match self {\n"); + for case in enum_.cases.iter() { self.push_str(&name); - self.push_str("\")\n"); - self.push_str(".field(\"code\", &(*self as i32))\n"); - self.push_str(".field(\"name\", &self.name())\n"); - self.push_str(".field(\"message\", &self.message())\n"); - self.push_str(".finish()\n"); - self.push_str("}\n"); - self.push_str("}\n"); + self.push_str("::"); + self.push_str(&case_name(&case.name)); + self.push_str(" => \""); + self.push_str(case.name.as_str()); + self.push_str("\",\n"); + } + self.push_str("}\n"); + self.push_str("}\n"); - self.push_str("impl std::fmt::Display for "); + self.push_str("pub fn message(&self) -> &'static str {\n"); + self.push_str("match self {\n"); + for case in enum_.cases.iter() { self.push_str(&name); - self.push_str( - "{\nfn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n", - ); - self.push_str("write!(f, \"{} (error {})\", self.name(), *self as i32)"); - self.push_str("}\n"); - self.push_str("}\n"); - self.push_str("\n"); - self.push_str("impl std::error::Error for "); - self.push_str(&name); - self.push_str("{}\n"); - } else { - self.push_str("impl"); - self.print_generics(&info, lt, true); - self.push_str(" std::fmt::Debug for "); - self.push_str(&name); - self.print_generics(&info, lt, false); - self.push_str(" {\n"); - self.push_str( - "fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n", - ); - self.push_str("match self {\n"); - for case in variant.cases.iter() { - self.push_str(&name); - self.push_str("::"); - self.push_str(&case_name(&case.name)); - if case.ty.is_some() { - self.push_str("(e)"); - } - self.push_str(" => {\n"); - self.push_str(&format!( - "f.debug_tuple(\"{}::{}\")", - name, - case_name(&case.name) - )); - if case.ty.is_some() { - self.push_str(".field(e)"); - } - self.push_str(".finish()\n"); - self.push_str("}\n"); + self.push_str("::"); + self.push_str(&case_name(&case.name)); + self.push_str(" => \""); + if let Some(contents) = &case.docs.contents { + self.push_str(contents.trim()); } - self.push_str("}\n"); - self.push_str("}\n"); + self.push_str("\",\n"); + } + self.push_str("}\n"); + self.push_str("}\n"); + + self.push_str("}\n"); + + self.push_str("impl std::fmt::Debug for "); + self.push_str(&name); + self.push_str( + "{\nfn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n", + ); + self.push_str("f.debug_struct(\""); + self.push_str(&name); + self.push_str("\")\n"); + self.push_str(".field(\"code\", &(*self as i32))\n"); + self.push_str(".field(\"name\", &self.name())\n"); + self.push_str(".field(\"message\", &self.message())\n"); + self.push_str(".finish()\n"); + self.push_str("}\n"); + self.push_str("}\n"); + + self.push_str("impl std::fmt::Display for "); + self.push_str(&name); + self.push_str( + "{\nfn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n", + ); + self.push_str("write!(f, \"{} (error {})\", self.name(), *self as i32)"); + self.push_str("}\n"); + self.push_str("}\n"); + self.push_str("\n"); + self.push_str("impl std::error::Error for "); + self.push_str(&name); + self.push_str("{}\n"); + } else { + self.push_str("impl"); + self.push_str(" std::fmt::Debug for "); + self.push_str(&name); + self.push_str(" {\n"); + self.push_str("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n"); + self.push_str("match self {\n"); + for case in enum_.cases.iter() { + self.push_str(&name); + self.push_str("::"); + self.push_str(&case_name(&case.name)); + self.push_str(" => {\n"); + self.push_str(&format!("\"{}::{}\".fmt(f)\n", name, case_name(&case.name))); self.push_str("}\n"); } + self.push_str("}\n"); + self.push_str("}\n"); + self.push_str("}\n"); } } @@ -810,18 +841,7 @@ pub trait RustFunctionGenerator { results: &mut Vec, blocks: Vec, ) { - // If this is a named enum with no type payloads and we're - // producing a singular result, then we know we're directly - // converting from the Rust enum to the integer discriminant. In - // this scenario we can optimize a bit and use just `as i32` - // instead of letting LLVM figure out it can do the same with - // optimizing the `match` generated below. let has_name = iface.types[id].name.is_some(); - if nresults == 1 && has_name && ty.cases.iter().all(|c| c.ty.is_none()) { - results.push(format!("{} as i32", operand)); - return; - } - self.let_results(nresults, results); self.push_str("match "); self.push_str(operand); diff --git a/crates/gen-spidermonkey/src/lib.rs b/crates/gen-spidermonkey/src/lib.rs index 50ae4ad87..3f8494d96 100644 --- a/crates/gen-spidermonkey/src/lib.rs +++ b/crates/gen-spidermonkey/src/lib.rs @@ -16,7 +16,7 @@ use wasm_encoder::Instruction; use wit_bindgen_gen_core::{ wit_parser::{ abi::{self, AbiVariant, WasmSignature, WasmType}, - Docs, Flags, Function, Interface, Record, ResourceId, SizeAlign, Tuple, Type, TypeId, + Docs, Enum, Flags, Function, Interface, Record, ResourceId, SizeAlign, Tuple, Type, TypeId, Variant, }, Direction, Files, Generator, @@ -988,6 +988,11 @@ impl Generator for SpiderMonkeyWasm<'_> { todo!() } + fn type_enum(&mut self, iface: &Interface, id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { + let _ = (iface, id, name, enum_, docs); + todo!() + } + fn type_resource(&mut self, iface: &Interface, ty: ResourceId) { let _ = (iface, ty); todo!() @@ -1926,6 +1931,16 @@ impl abi::Bindgen for Bindgen<'_, '_> { name: _, ty: _, } => todo!(), + abi::Instruction::EnumLower { + enum_: _, + name: _, + ty: _, + } => todo!(), + abi::Instruction::EnumLift { + enum_: _, + name: _, + ty: _, + } => todo!(), abi::Instruction::CallWasm { module, name, sig } => { // Push the Wasm arguments. // diff --git a/crates/gen-wasmtime-py/src/lib.rs b/crates/gen-wasmtime-py/src/lib.rs index a44937e01..362a154ca 100644 --- a/crates/gen-wasmtime-py/src/lib.rs +++ b/crates/gen-wasmtime-py/src/lib.rs @@ -411,7 +411,9 @@ impl WasmtimePy { match &ty.kind { TypeDefKind::Type(t) => self.print_ty(iface, t), TypeDefKind::Tuple(t) => self.print_tuple(iface, t), - TypeDefKind::Record(_) | TypeDefKind::Flags(_) => unreachable!(), + TypeDefKind::Record(_) | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => { + unreachable!() + } TypeDefKind::Variant(v) => { if let Some(t) = v.as_option() { self.pyimport("typing", "Optional"); @@ -645,27 +647,7 @@ impl Generator for WasmtimePy { docs: &Docs, ) { self.docs(docs); - if variant.is_enum() { - self.pyimport("enum", "Enum"); - self.src - .push_str(&format!("class {}(Enum):\n", name.to_camel_case())); - self.indent(); - for (i, case) in variant.cases.iter().enumerate() { - self.docs(&case.docs); - - // TODO this handling of digits should be more general and - // shouldn't be here just to fix the one case in wasi where an - // enum variant is "2big" and doesn't generate valid Python. We - // should probably apply this to all generated Python - // identifiers. - let mut name = case.name.to_shouty_snake_case(); - if name.chars().next().unwrap().is_digit(10) { - name = format!("_{}", name); - } - self.src.push_str(&format!("{} = {}\n", name, i)); - } - self.deindent(); - } else if let Some(t) = variant.as_option() { + if let Some(t) = variant.as_option() { self.pyimport("typing", "Optional"); self.src .push_str(&format!("{} = Optional[", name.to_camel_case())); @@ -717,6 +699,37 @@ impl Generator for WasmtimePy { self.src.push_str("\n"); } + fn type_enum( + &mut self, + _iface: &Interface, + _id: TypeId, + name: &str, + enum_: &Enum, + docs: &Docs, + ) { + self.docs(docs); + self.pyimport("enum", "Enum"); + self.src + .push_str(&format!("class {}(Enum):\n", name.to_camel_case())); + self.indent(); + for (i, case) in enum_.cases.iter().enumerate() { + self.docs(&case.docs); + + // TODO this handling of digits should be more general and + // shouldn't be here just to fix the one case in wasi where an + // enum variant is "2big" and doesn't generate valid Python. We + // should probably apply this to all generated Python + // identifiers. + let mut name = case.name.to_shouty_snake_case(); + if name.chars().next().unwrap().is_digit(10) { + name = format!("_{}", name); + } + self.src.push_str(&format!("{} = {}\n", name, i)); + } + self.deindent(); + self.src.push_str("\n"); + } + fn type_resource(&mut self, _iface: &Interface, _ty: ResourceId) { // if !self.in_import { // self.exported_resources.insert(ty); @@ -1572,6 +1585,7 @@ impl Bindgen for FunctionBindgen<'_> { results.push(name.clone()); self.payloads.push(name); } + Instruction::VariantLower { variant, results: result_types, @@ -1587,12 +1601,6 @@ impl Bindgen for FunctionBindgen<'_> { .drain(self.payloads.len() - variant.cases.len()..) .collect::>(); - if result_types.len() == 1 && variant.is_enum() { - if name.is_some() { - return results.push(format!("({}).value", operands[0])); - } - } - for _ in 0..result_types.len() { results.push(self.locals.tmp("variant")); } @@ -1614,9 +1622,6 @@ impl Bindgen for FunctionBindgen<'_> { if i == 0 { self.src.push_str(&format!("{} is None:\n", operands[0])); } - } else if variant.is_enum() && variant.as_expected().is_none() { - self.src - .push_str(&format!("{}.value == {}:\n", operands[0], i)); } else { self.src.push_str(&format!( "isinstance({}, {}{}):\n", @@ -1677,13 +1682,6 @@ impl Bindgen for FunctionBindgen<'_> { .drain(self.blocks.len() - variant.cases.len()..) .collect::>(); - if let Some(name) = name { - if variant.is_enum() { - results.push(format!("{}({})", name.to_camel_case(), operands[0])); - return; - } - } - let result = self.locals.tmp("variant"); self.src.push_str(&format!( "{}: {}\n", @@ -1755,6 +1753,12 @@ impl Bindgen for FunctionBindgen<'_> { results.push(result); } + Instruction::EnumLower { .. } => results.push(format!("({}).value", operands[0])), + + Instruction::EnumLift { name, .. } => { + results.push(format!("{}({})", name.to_camel_case(), operands[0])); + } + Instruction::ListCanonLower { element, realloc } => { // Lowering only happens when we're passing lists into wasm, // which forces us to always allocate, so this should always be diff --git a/crates/gen-wasmtime/src/lib.rs b/crates/gen-wasmtime/src/lib.rs index 63af19557..b809fef84 100644 --- a/crates/gen-wasmtime/src/lib.rs +++ b/crates/gen-wasmtime/src/lib.rs @@ -434,11 +434,22 @@ impl Generator for Wasmtime { &mut self, iface: &Interface, id: TypeId, - name: &str, + _name: &str, variant: &Variant, docs: &Docs, ) { - self.print_typedef_variant(iface, id, name, variant, docs); + self.print_typedef_variant(iface, id, variant, docs); + } + + fn type_enum( + &mut self, + _iface: &Interface, + _id: TypeId, + name: &str, + enum_: &Enum, + docs: &Docs, + ) { + self.print_typedef_enum(name, enum_, docs); } fn type_resource(&mut self, iface: &Interface, ty: ResourceId) { @@ -1638,6 +1649,30 @@ impl Bindgen for FunctionBindgen<'_> { self.gen.needs_invalid_variant = true; } + Instruction::EnumLower { .. } => { + results.push(format!("{} as i32", operands[0])); + } + + Instruction::EnumLift { name, enum_, .. } => { + let mut result = format!("match "); + result.push_str(&operands[0]); + result.push_str(" {\n"); + for (i, case) in enum_.cases.iter().enumerate() { + result.push_str(&i.to_string()); + result.push_str(" => "); + result.push_str(&name.to_camel_case()); + result.push_str("::"); + result.push_str(&wit_bindgen_gen_rust::case_name(&case.name)); + result.push_str(",\n"); + } + result.push_str("_ => return Err(invalid_variant(\""); + result.push_str(&name.to_camel_case()); + result.push_str("\")),\n"); + result.push_str("}"); + results.push(result); + self.gen.needs_invalid_variant = true; + } + Instruction::ListCanonLower { element, realloc } => { // Lowering only happens when we're passing lists into wasm, // which forces us to always allocate, so this should always be diff --git a/crates/parser/src/abi.rs b/crates/parser/src/abi.rs index 388286306..1ad6c228a 100644 --- a/crates/parser/src/abi.rs +++ b/crates/parser/src/abi.rs @@ -1,6 +1,6 @@ use crate::sizealign::align_to; use crate::{ - Flags, FlagsRepr, Function, Int, Interface, Record, ResourceId, Tuple, Type, TypeDefKind, + Enum, Flags, FlagsRepr, Function, Int, Interface, Record, ResourceId, Tuple, Type, TypeDefKind, TypeId, Variant, }; use std::mem; @@ -568,6 +568,20 @@ def_instruction! { ty: TypeId, } : [1] => [1], + /// Pops an enum off the stack and pushes the `i32` representation. + EnumLower { + enum_: &'a Enum, + name: &'a str, + ty: TypeId, + } : [1] => [1], + + /// Pops an `i32` off the stack and lifts it into the `enum` specified. + EnumLift { + enum_: &'a Enum, + name: &'a str, + ty: TypeId, + } : [1] => [1], + // calling/control flow /// Represents a call to a raw WebAssembly API. The module/name are @@ -967,6 +981,8 @@ impl Interface { } } } + + TypeDefKind::Enum(e) => result.push(e.tag().into()), }, } } @@ -1510,6 +1526,13 @@ impl<'a, B: Bindgen> Generator<'a, B> { name: self.iface.types[id].name.as_deref(), }); } + TypeDefKind::Enum(enum_) => { + self.emit(&EnumLower { + enum_, + ty: id, + name: self.iface.types[id].name.as_deref().unwrap(), + }); + } }, } } @@ -1666,6 +1689,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { name: self.iface.types[id].name.as_deref(), }); } + + TypeDefKind::Enum(enum_) => { + self.emit(&EnumLift { + enum_, + ty: id, + name: self.iface.types[id].name.as_deref().unwrap(), + }); + } }, } } @@ -1770,6 +1801,12 @@ impl<'a, B: Bindgen> Generator<'a, B> { name: self.iface.types[id].name.as_deref(), }); } + + TypeDefKind::Enum(e) => { + self.lower(ty); + self.stack.push(addr); + self.store_intrepr(offset, e.tag()); + } }, } } @@ -1896,6 +1933,12 @@ impl<'a, B: Bindgen> Generator<'a, B> { name: self.iface.types[id].name.as_deref(), }); } + + TypeDefKind::Enum(e) => { + self.stack.push(addr.clone()); + self.load_intrepr(offset, e.tag()); + self.lift(ty); + } }, } } diff --git a/crates/parser/src/ast.rs b/crates/parser/src/ast.rs index 85ecaccac..3f1499847 100644 --- a/crates/parser/src/ast.rs +++ b/crates/parser/src/ast.rs @@ -94,6 +94,7 @@ enum Type<'a> { Flags(Flags<'a>), Variant(Variant<'a>), Tuple(Vec>), + Enum(Enum<'a>), } struct Record<'a> { @@ -127,6 +128,16 @@ struct Case<'a> { ty: Option>, } +struct Enum<'a> { + span: Span, + cases: Vec>, +} + +struct EnumCase<'a> { + docs: Docs<'a>, + name: Id<'a>, +} + pub struct Value<'a> { docs: Docs<'a>, name: Id<'a>, @@ -330,8 +341,7 @@ impl<'a> TypeDef<'a> { fn parse_enum(tokens: &mut Tokenizer<'a>, docs: Docs<'a>) -> Result { tokens.expect(Token::Enum)?; let name = parse_id(tokens)?; - let ty = Type::Variant(Variant { - tag: None, + let ty = Type::Enum(Enum { span: name.span, cases: parse_list( tokens, @@ -339,11 +349,7 @@ impl<'a> TypeDef<'a> { Token::RightBrace, |docs, tokens| { let name = parse_id(tokens)?; - Ok(Case { - docs, - name, - ty: None, - }) + Ok(EnumCase { docs, name }) }, )?, }); diff --git a/crates/parser/src/ast/resolve.rs b/crates/parser/src/ast/resolve.rs index 1a021b57f..0af4c9831 100644 --- a/crates/parser/src/ast/resolve.rs +++ b/crates/parser/src/ast/resolve.rs @@ -23,6 +23,7 @@ enum Key { Record(Vec<(String, Type)>), Flags(Vec), Tuple(Vec), + Enum(Vec), List(Type), } @@ -225,6 +226,9 @@ impl Resolver { .collect(), tag: v.tag, }), + TypeDefKind::Enum(e) => TypeDefKind::Enum(Enum { + cases: e.cases.clone(), + }), TypeDefKind::List(t) => TypeDefKind::List(self.copy_type(dep_name, dep, *t)), }, }; @@ -423,6 +427,26 @@ impl Resolver { cases, }) } + super::Type::Enum(e) => { + if e.cases.is_empty() { + return Err(Error { + span: e.span, + msg: "empty enum".to_string(), + } + .into()); + } + let cases = e + .cases + .iter() + .map(|case| { + Ok(EnumCase { + docs: self.docs(&case.docs), + name: case.name.name.to_string(), + }) + }) + .collect::>>()?; + TypeDefKind::Enum(Enum { cases }) + } }) } @@ -481,6 +505,9 @@ impl Resolver { Key::Flags(r.flags.iter().map(|f| f.name.clone()).collect::>()) } TypeDefKind::Tuple(t) => Key::Tuple(t.types.clone()), + TypeDefKind::Enum(r) => { + Key::Enum(r.cases.iter().map(|f| f.name.clone()).collect::>()) + } TypeDefKind::List(ty) => Key::List(*ty), }; let types = &mut self.types; @@ -647,7 +674,10 @@ impl Resolver { } } - TypeDefKind::Flags(_) | TypeDefKind::List(_) | TypeDefKind::Type(_) => {} + TypeDefKind::Flags(_) + | TypeDefKind::List(_) + | TypeDefKind::Type(_) + | TypeDefKind::Enum(_) => {} } valid.insert(ty); diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 0c79951af..54c6ea0d8 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -49,6 +49,7 @@ pub enum TypeDefKind { Flags(Flags), Tuple(Tuple), Variant(Variant), + Enum(Enum), List(Type), Type(Type), } @@ -162,10 +163,6 @@ impl Variant { } } - pub fn is_enum(&self) -> bool { - self.cases.iter().all(|c| c.ty.is_none()) - } - pub fn is_union(&self) -> bool { self.cases .iter() @@ -200,6 +197,28 @@ impl Variant { } } +#[derive(Debug)] +pub struct Enum { + pub cases: Vec, +} + +#[derive(Debug, Clone)] +pub struct EnumCase { + pub docs: Docs, + pub name: String, +} + +impl Enum { + pub fn tag(&self) -> Int { + match self.cases.len() { + n if n <= u8::max_value() as usize => Int::U8, + n if n <= u16::max_value() as usize => Int::U16, + n if n <= u32::max_value() as usize => Int::U32, + _ => panic!("too many cases to fit in a repr"), + } + } +} + #[derive(Clone, Default, Debug)] pub struct Docs { pub contents: Option, @@ -380,7 +399,7 @@ impl Interface { return; } match &self.types[id].kind { - TypeDefKind::Flags(_) => {} + TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => {} TypeDefKind::Type(t) | TypeDefKind::List(t) => self.topo_visit_ty(t, list, visited), TypeDefKind::Record(r) => { for f in r.fields.iter() { @@ -426,7 +445,7 @@ impl Interface { Type::Bool | Type::Char | Type::Handle(_) | Type::String => false, Type::Id(id) => match &self.types[*id].kind { - TypeDefKind::List(_) | TypeDefKind::Variant(_) => false, + TypeDefKind::List(_) | TypeDefKind::Variant(_) | TypeDefKind::Enum(_) => false, TypeDefKind::Type(t) => self.all_bits_valid(t), TypeDefKind::Record(r) => r.fields.iter().all(|f| self.all_bits_valid(&f.ty)), TypeDefKind::Tuple(t) => t.types.iter().all(|t| self.all_bits_valid(t)), diff --git a/crates/parser/src/sizealign.rs b/crates/parser/src/sizealign.rs index 9edb95066..46db20a41 100644 --- a/crates/parser/src/sizealign.rs +++ b/crates/parser/src/sizealign.rs @@ -39,6 +39,7 @@ impl SizeAlign { } (size, align) } + TypeDefKind::Enum(e) => int_size_align(e.tag()), } } diff --git a/crates/parser/tests/all.rs b/crates/parser/tests/all.rs index b3f64bcd1..0246a396f 100644 --- a/crates/parser/tests/all.rs +++ b/crates/parser/tests/all.rs @@ -194,6 +194,9 @@ fn to_json(i: &Interface) -> String { Flags { flags: Vec, }, + Enum { + cases: Vec, + }, Variant { cases: Vec<(String, Option)>, }, @@ -280,6 +283,9 @@ fn to_json(i: &Interface) -> String { TypeDefKind::Flags(r) => Type::Flags { flags: r.flags.iter().map(|f| f.name.clone()).collect(), }, + TypeDefKind::Enum(r) => Type::Enum { + cases: r.cases.iter().map(|f| f.name.clone()).collect(), + }, TypeDefKind::Variant(v) => Type::Variant { cases: v .cases diff --git a/crates/parser/tests/ui/parse-fail/empty-variant2.wit b/crates/parser/tests/ui/parse-fail/empty-enum.wit similarity index 100% rename from crates/parser/tests/ui/parse-fail/empty-variant2.wit rename to crates/parser/tests/ui/parse-fail/empty-enum.wit diff --git a/crates/parser/tests/ui/parse-fail/empty-enum.wit.result b/crates/parser/tests/ui/parse-fail/empty-enum.wit.result new file mode 100644 index 000000000..02d4389d4 --- /dev/null +++ b/crates/parser/tests/ui/parse-fail/empty-enum.wit.result @@ -0,0 +1,5 @@ +empty enum + --> tests/ui/parse-fail/empty-enum.wit:2:6 + | + 2 | enum t {} + | ^ \ No newline at end of file diff --git a/crates/parser/tests/ui/parse-fail/empty-variant2.wit.result b/crates/parser/tests/ui/parse-fail/empty-variant2.wit.result deleted file mode 100644 index 7b26f25d0..000000000 --- a/crates/parser/tests/ui/parse-fail/empty-variant2.wit.result +++ /dev/null @@ -1,5 +0,0 @@ -empty variant - --> tests/ui/parse-fail/empty-variant2.wit:2:6 - | - 2 | enum t {} - | ^ \ No newline at end of file diff --git a/crates/parser/tests/ui/types.wit.result b/crates/parser/tests/ui/types.wit.result index a3d56ef78..2213e764d 100644 --- a/crates/parser/tests/ui/types.wit.result +++ b/crates/parser/tests/ui/types.wit.result @@ -432,40 +432,22 @@ { "idx": 42, "name": "t41", - "variant": { + "enum": { "cases": [ - [ - "a", - null - ], - [ - "b", - null - ], - [ - "c", - null - ] + "a", + "b", + "c" ] } }, { "idx": 43, "name": "t42", - "variant": { + "enum": { "cases": [ - [ - "a", - null - ], - [ - "b", - null - ], - [ - "c", - null - ] + "a", + "b", + "c" ] } }, diff --git a/crates/parser/tests/ui/wasi-clock.wit.result b/crates/parser/tests/ui/wasi-clock.wit.result index 5bc0882fa..3cbfe5895 100644 --- a/crates/parser/tests/ui/wasi-clock.wit.result +++ b/crates/parser/tests/ui/wasi-clock.wit.result @@ -3,16 +3,10 @@ { "idx": 0, "name": "clockid", - "variant": { + "enum": { "cases": [ - [ - "realtime", - null - ], - [ - "monotonic", - null - ] + "realtime", + "monotonic" ] }, "foreign_module": "wasi" @@ -26,316 +20,85 @@ { "idx": 2, "name": "errno", - "variant": { + "enum": { "cases": [ - [ - "success", - null - ], - [ - "toobig", - null - ], - [ - "access", - null - ], - [ - "addrinuse", - null - ], - [ - "addrnotavail", - null - ], - [ - "afnosupport", - null - ], - [ - "again", - null - ], - [ - "already", - null - ], - [ - "badf", - null - ], - [ - "badmsg", - null - ], - [ - "busy", - null - ], - [ - "canceled", - null - ], - [ - "child", - null - ], - [ - "connaborted", - null - ], - [ - "connrefused", - null - ], - [ - "connreset", - null - ], - [ - "deadlk", - null - ], - [ - "destaddrreq", - null - ], - [ - "dom", - null - ], - [ - "dquot", - null - ], - [ - "exist", - null - ], - [ - "fault", - null - ], - [ - "fbig", - null - ], - [ - "hostunreach", - null - ], - [ - "idrm", - null - ], - [ - "ilseq", - null - ], - [ - "inprogress", - null - ], - [ - "intr", - null - ], - [ - "inval", - null - ], - [ - "io", - null - ], - [ - "isconn", - null - ], - [ - "isdir", - null - ], - [ - "loop", - null - ], - [ - "mfile", - null - ], - [ - "mlink", - null - ], - [ - "msgsize", - null - ], - [ - "multihop", - null - ], - [ - "nametoolong", - null - ], - [ - "netdown", - null - ], - [ - "netreset", - null - ], - [ - "netunreach", - null - ], - [ - "nfile", - null - ], - [ - "nobufs", - null - ], - [ - "nodev", - null - ], - [ - "noent", - null - ], - [ - "noexec", - null - ], - [ - "nolck", - null - ], - [ - "nolink", - null - ], - [ - "nomem", - null - ], - [ - "nomsg", - null - ], - [ - "noprotoopt", - null - ], - [ - "nospc", - null - ], - [ - "nosys", - null - ], - [ - "notconn", - null - ], - [ - "notdir", - null - ], - [ - "notempty", - null - ], - [ - "notrecoverable", - null - ], - [ - "notsock", - null - ], - [ - "notsup", - null - ], - [ - "notty", - null - ], - [ - "nxio", - null - ], - [ - "overflow", - null - ], - [ - "ownerdead", - null - ], - [ - "perm", - null - ], - [ - "pipe", - null - ], - [ - "proto", - null - ], - [ - "protonosupport", - null - ], - [ - "prototype", - null - ], - [ - "range", - null - ], - [ - "rofs", - null - ], - [ - "spipe", - null - ], - [ - "srch", - null - ], - [ - "stale", - null - ], - [ - "timedout", - null - ], - [ - "txtbsy", - null - ], - [ - "xdev", - null - ], - [ - "notcapable", - null - ] + "success", + "toobig", + "access", + "addrinuse", + "addrnotavail", + "afnosupport", + "again", + "already", + "badf", + "badmsg", + "busy", + "canceled", + "child", + "connaborted", + "connrefused", + "connreset", + "deadlk", + "destaddrreq", + "dom", + "dquot", + "exist", + "fault", + "fbig", + "hostunreach", + "idrm", + "ilseq", + "inprogress", + "intr", + "inval", + "io", + "isconn", + "isdir", + "loop", + "mfile", + "mlink", + "msgsize", + "multihop", + "nametoolong", + "netdown", + "netreset", + "netunreach", + "nfile", + "nobufs", + "nodev", + "noent", + "noexec", + "nolck", + "nolink", + "nomem", + "nomsg", + "noprotoopt", + "nospc", + "nosys", + "notconn", + "notdir", + "notempty", + "notrecoverable", + "notsock", + "notsup", + "notty", + "nxio", + "overflow", + "ownerdead", + "perm", + "pipe", + "proto", + "protonosupport", + "prototype", + "range", + "rofs", + "spipe", + "srch", + "stale", + "timedout", + "txtbsy", + "xdev", + "notcapable" ] }, "foreign_module": "wasi" diff --git a/crates/parser/tests/ui/wasi-http.wit.result b/crates/parser/tests/ui/wasi-http.wit.result index 396167973..7142b2a35 100644 --- a/crates/parser/tests/ui/wasi-http.wit.result +++ b/crates/parser/tests/ui/wasi-http.wit.result @@ -33,16 +33,10 @@ { "idx": 4, "name": "error", - "variant": { + "enum": { "cases": [ - [ - "overflow", - null - ], - [ - "unavailable", - null - ] + "overflow", + "unavailable" ] } }, diff --git a/crates/parser/tests/ui/wasi.wit.result b/crates/parser/tests/ui/wasi.wit.result index 7fe8e0a7f..b9f495b9f 100644 --- a/crates/parser/tests/ui/wasi.wit.result +++ b/crates/parser/tests/ui/wasi.wit.result @@ -3,16 +3,10 @@ { "idx": 0, "name": "clockid", - "variant": { + "enum": { "cases": [ - [ - "realtime", - null - ], - [ - "monotonic", - null - ] + "realtime", + "monotonic" ] } }, @@ -24,316 +18,85 @@ { "idx": 2, "name": "errno", - "variant": { + "enum": { "cases": [ - [ - "success", - null - ], - [ - "toobig", - null - ], - [ - "access", - null - ], - [ - "addrinuse", - null - ], - [ - "addrnotavail", - null - ], - [ - "afnosupport", - null - ], - [ - "again", - null - ], - [ - "already", - null - ], - [ - "badf", - null - ], - [ - "badmsg", - null - ], - [ - "busy", - null - ], - [ - "canceled", - null - ], - [ - "child", - null - ], - [ - "connaborted", - null - ], - [ - "connrefused", - null - ], - [ - "connreset", - null - ], - [ - "deadlk", - null - ], - [ - "destaddrreq", - null - ], - [ - "dom", - null - ], - [ - "dquot", - null - ], - [ - "exist", - null - ], - [ - "fault", - null - ], - [ - "fbig", - null - ], - [ - "hostunreach", - null - ], - [ - "idrm", - null - ], - [ - "ilseq", - null - ], - [ - "inprogress", - null - ], - [ - "intr", - null - ], - [ - "inval", - null - ], - [ - "io", - null - ], - [ - "isconn", - null - ], - [ - "isdir", - null - ], - [ - "loop", - null - ], - [ - "mfile", - null - ], - [ - "mlink", - null - ], - [ - "msgsize", - null - ], - [ - "multihop", - null - ], - [ - "nametoolong", - null - ], - [ - "netdown", - null - ], - [ - "netreset", - null - ], - [ - "netunreach", - null - ], - [ - "nfile", - null - ], - [ - "nobufs", - null - ], - [ - "nodev", - null - ], - [ - "noent", - null - ], - [ - "noexec", - null - ], - [ - "nolck", - null - ], - [ - "nolink", - null - ], - [ - "nomem", - null - ], - [ - "nomsg", - null - ], - [ - "noprotoopt", - null - ], - [ - "nospc", - null - ], - [ - "nosys", - null - ], - [ - "notconn", - null - ], - [ - "notdir", - null - ], - [ - "notempty", - null - ], - [ - "notrecoverable", - null - ], - [ - "notsock", - null - ], - [ - "notsup", - null - ], - [ - "notty", - null - ], - [ - "nxio", - null - ], - [ - "overflow", - null - ], - [ - "ownerdead", - null - ], - [ - "perm", - null - ], - [ - "pipe", - null - ], - [ - "proto", - null - ], - [ - "protonosupport", - null - ], - [ - "prototype", - null - ], - [ - "range", - null - ], - [ - "rofs", - null - ], - [ - "spipe", - null - ], - [ - "srch", - null - ], - [ - "stale", - null - ], - [ - "timedout", - null - ], - [ - "txtbsy", - null - ], - [ - "xdev", - null - ], - [ - "notcapable", - null - ] + "success", + "toobig", + "access", + "addrinuse", + "addrnotavail", + "afnosupport", + "again", + "already", + "badf", + "badmsg", + "busy", + "canceled", + "child", + "connaborted", + "connrefused", + "connreset", + "deadlk", + "destaddrreq", + "dom", + "dquot", + "exist", + "fault", + "fbig", + "hostunreach", + "idrm", + "ilseq", + "inprogress", + "intr", + "inval", + "io", + "isconn", + "isdir", + "loop", + "mfile", + "mlink", + "msgsize", + "multihop", + "nametoolong", + "netdown", + "netreset", + "netunreach", + "nfile", + "nobufs", + "nodev", + "noent", + "noexec", + "nolck", + "nolink", + "nomem", + "nomsg", + "noprotoopt", + "nospc", + "nosys", + "notconn", + "notdir", + "notempty", + "notrecoverable", + "notsock", + "notsup", + "notty", + "nxio", + "overflow", + "ownerdead", + "perm", + "pipe", + "proto", + "protonosupport", + "prototype", + "range", + "rofs", + "spipe", + "srch", + "stale", + "timedout", + "txtbsy", + "xdev", + "notcapable" ] } } diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 864c89364..a10556863 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -180,6 +180,7 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { quote::quote! { Vec<#t> } } TypeDefKind::Flags(_) => panic!("unknown flags"), + TypeDefKind::Enum(_) => panic!("unknown enum"), TypeDefKind::Record(_) => panic!("unknown record"), TypeDefKind::Tuple(t) => { let fields = t.types.iter().map(|ty| quote_ty(param, iface, ty)); diff --git a/crates/wasmlink/src/adapter/call.rs b/crates/wasmlink/src/adapter/call.rs index d775ef3e6..ebafaec3b 100644 --- a/crates/wasmlink/src/adapter/call.rs +++ b/crates/wasmlink/src/adapter/call.rs @@ -482,7 +482,7 @@ impl<'a> CallAdapter<'a> { } } - TypeDefKind::Variant(v) if v.is_enum() => { + TypeDefKind::Enum(_) => { params.next().unwrap(); } TypeDefKind::Variant(v) => { @@ -650,8 +650,7 @@ impl<'a> CallAdapter<'a> { ); } } - - TypeDefKind::Variant(v) if v.is_enum() => {} + TypeDefKind::Enum(_) => {} TypeDefKind::Variant(v) => { let payload_offset = sizes.payload_offset(v) as u32; diff --git a/crates/wasmlink/src/module.rs b/crates/wasmlink/src/module.rs index 9ea4d7ced..b0142d523 100644 --- a/crates/wasmlink/src/module.rs +++ b/crates/wasmlink/src/module.rs @@ -56,6 +56,7 @@ fn has_list(interface: &WitInterface, ty: &WitType) -> bool { .map(|t| has_list(interface, t)) .unwrap_or(false) }), + TypeDefKind::Enum(_) => false, }, _ => false, } diff --git a/crates/wit-component/src/decoding.rs b/crates/wit-component/src/decoding.rs index 1e3b85fd7..f4e3d5d8d 100644 --- a/crates/wit-component/src/decoding.rs +++ b/crates/wit-component/src/decoding.rs @@ -4,10 +4,7 @@ use wasmparser::{ types, Chunk, ComponentExportKind, Encoding, Parser, Payload, PrimitiveInterfaceType, Validator, WasmFeatures, }; -use wit_parser::{ - validate_id, Case, Docs, Field, Flag, Flags, Function, FunctionKind, Interface, Record, Tuple, - Type, TypeDef, TypeDefKind, TypeId, Variant, -}; +use wit_parser::*; /// Represents information about a decoded WebAssembly component. pub struct ComponentInfo<'a> { @@ -466,10 +463,7 @@ impl<'a> InterfaceDecoder<'a> { names: impl ExactSizeIterator, ) -> Result { let enum_name = enum_name.ok_or_else(|| anyhow!("interface has an unnamed enum type"))?; - - let names_len = names.len(); - - let variant = Variant { + let enum_ = Enum { cases: names .map(|name| { validate_id(name).with_context(|| { @@ -479,20 +473,17 @@ impl<'a> InterfaceDecoder<'a> { ) })?; - Ok(Case { + Ok(EnumCase { docs: Docs::default(), name: name.to_string(), - ty: None, }) }) .collect::>()?, - tag: Variant::infer_tag(names_len), }; - Ok(Type::Id(self.alloc_type( - Some(enum_name), - TypeDefKind::Variant(variant), - ))) + Ok(Type::Id( + self.alloc_type(Some(enum_name), TypeDefKind::Enum(enum_)), + )) } fn decode_union( diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index fe5babaa4..88fc9af40 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -13,7 +13,8 @@ use wasm_encoder::*; use wasmparser::{Validator, WasmFeatures}; use wit_parser::{ abi::{AbiVariant, WasmSignature, WasmType}, - Flags, Function, FunctionKind, Interface, Record, Tuple, Type, TypeDef, TypeDefKind, Variant, + Enum, Flags, Function, FunctionKind, Interface, Record, Tuple, Type, TypeDef, TypeDefKind, + Variant, }; const INDIRECT_TABLE_NAME: &str = "$imports"; @@ -135,6 +136,16 @@ impl PartialEq for TypeDefKey<'_> { })) }) } + (TypeDefKind::Enum(e1), TypeDefKind::Enum(e2)) => { + if e1.cases.len() != e2.cases.len() { + return false; + } + + e1.cases + .iter() + .zip(e2.cases.iter()) + .all(|(c1, c2)| c1.name == c2.name) + } (TypeDefKind::List(t1), TypeDefKind::List(t2)) | (TypeDefKind::Type(t1), TypeDefKind::Type(t2)) => TypeKey { interface: self.interface, @@ -194,8 +205,14 @@ impl Hash for TypeDefKey<'_> { .hash(state); } } - TypeDefKind::List(ty) => { + TypeDefKind::Enum(e) => { state.write_u8(4); + for c in &e.cases { + c.name.hash(state); + } + } + TypeDefKind::List(ty) => { + state.write_u8(5); TypeKey { interface: self.interface, ty: *ty, @@ -203,7 +220,7 @@ impl Hash for TypeDefKey<'_> { .hash(state); } TypeDefKind::Type(ty) => { - state.write_u8(5); + state.write_u8(6); TypeKey { interface: self.interface, ty: *ty, @@ -390,6 +407,7 @@ impl<'a> TypeEncoder<'a> { TypeDefKind::Tuple(t) => self.encode_tuple(interface, instance, t)?, TypeDefKind::Flags(r) => self.encode_flags(r)?, TypeDefKind::Variant(v) => self.encode_variant(interface, instance, v)?, + TypeDefKind::Enum(e) => self.encode_enum(e)?, TypeDefKind::List(ty) => { let ty = self.encode_type(interface, instance, ty)?; let index = self.types.len(); @@ -506,13 +524,6 @@ impl<'a> TypeEncoder<'a> { return Ok(InterfaceTypeRef::Type(index)); } - if variant.is_enum() { - let index = self.types.len(); - let encoder = self.types.interface_type(); - encoder.enum_type(variant.cases.iter().map(|c| c.name.as_str())); - return Ok(InterfaceTypeRef::Type(index)); - } - if variant.is_union() { let tys = variant .cases @@ -547,6 +558,13 @@ impl<'a> TypeEncoder<'a> { Ok(InterfaceTypeRef::Type(index)) } + fn encode_enum(&mut self, enum_: &Enum) -> Result { + let index = self.types.len(); + let encoder = self.types.interface_type(); + encoder.enum_type(enum_.cases.iter().map(|c| c.name.as_str())); + Ok(InterfaceTypeRef::Type(index)) + } + fn export_type( &mut self, instance_ty: &mut Option>, @@ -617,6 +635,7 @@ impl RequiredOptions { TypeDefKind::Variant(v) => { Self::for_types(interface, v.cases.iter().filter_map(|c| c.ty.as_ref())) } + TypeDefKind::Enum(_) => Self::None, TypeDefKind::List(t) => { // Lists need at least the `into` option, but may require // the encoding option if there's a string somewhere in the diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index 9124782d5..28aafbe97 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Result}; use std::collections::HashSet; use std::fmt::Write; -use wit_parser::{Flags, Interface, Record, Tuple, Type, TypeDefKind, TypeId, Variant}; +use wit_parser::{Enum, Flags, Interface, Record, Tuple, Type, TypeDefKind, TypeId, Variant}; /// A utility for printing WebAssembly interface definitions to a string. #[derive(Default)] @@ -78,6 +78,9 @@ impl InterfacePrinter { TypeDefKind::Flags(_) => { bail!("interface has unnamed flags type") } + TypeDefKind::Enum(_) => { + bail!("interface has unnamed enum type") + } TypeDefKind::Variant(v) => { self.print_variant_type(interface, v)?; } @@ -169,6 +172,7 @@ impl InterfacePrinter { TypeDefKind::Variant(v) => { self.declare_variant(interface, ty.name.as_deref(), v)? } + TypeDefKind::Enum(e) => self.declare_enum(ty.name.as_deref(), e)?, TypeDefKind::List(inner) => { self.declare_list(interface, ty.name.as_deref(), inner)? } @@ -269,15 +273,6 @@ impl InterfacePrinter { match name { Some(name) => { - if variant.is_enum() { - writeln!(&mut self.output, "enum {} {{", name)?; - for case in &variant.cases { - writeln!(&mut self.output, " {},", case.name)?; - } - self.output.push_str("}\n\n"); - return Ok(()); - } - if variant.is_union() { writeln!(&mut self.output, "union {} {{", name)?; for case in &variant.cases { @@ -306,6 +301,19 @@ impl InterfacePrinter { } } + fn declare_enum(&mut self, name: Option<&str>, enum_: &Enum) -> Result<()> { + let name = match name { + Some(name) => name, + None => bail!("interface has unnamed enum type"), + }; + writeln!(&mut self.output, "enum {} {{", name)?; + for case in &enum_.cases { + writeln!(&mut self.output, " {},", case.name)?; + } + self.output.push_str("}\n\n"); + Ok(()) + } + fn declare_list(&mut self, interface: &Interface, name: Option<&str>, ty: &Type) -> Result<()> { self.declare_type(interface, ty)?; diff --git a/tests/runtime/flavorful/wasm.c b/tests/runtime/flavorful/wasm.c index 3c7b0649d..deefddedd 100644 --- a/tests/runtime/flavorful/wasm.c +++ b/tests/runtime/flavorful/wasm.c @@ -92,8 +92,8 @@ void exports_test_imports() { imports_list_expected_void_void_t b; imports_expected_void_void_t b_val[2]; - b_val[0] = 0; - b_val[1] = 1; + b_val[0].tag = 0; + b_val[1].tag = 1; b.ptr = b_val; b.len = 2; @@ -114,8 +114,8 @@ void exports_test_imports() { assert(d.ptr[1] == true); assert(e.len == 2); - assert(e.ptr[0] == 1); - assert(e.ptr[1] == 0); + assert(e.ptr[0].tag == 1); + assert(e.ptr[1].tag == 0); assert(f.len == 2); assert(f.ptr[0] == IMPORTS_MY_ERRNO_A); diff --git a/tests/runtime/variants/wasm.c b/tests/runtime/variants/wasm.c index 015ff1aee..184ff9981 100644 --- a/tests/runtime/variants/wasm.c +++ b/tests/runtime/variants/wasm.c @@ -157,7 +157,7 @@ void exports_test_imports() { imports_my_errno_t c; imports_variant_enums(true, 0, IMPORTS_MY_ERRNO_SUCCESS, &a, &b, &c); assert(a == false); - assert(b == 1); + assert(b.tag == 1); assert(c == IMPORTS_MY_ERRNO_A); } }