diff --git a/Cargo.lock b/Cargo.lock index 271326b7e..399ab0d5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1635,15 +1635,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "wasm-encoder" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f0c17267a5ffd6ae3d897589460e21db1673c84fb7016b909c9691369a75ea" -dependencies = [ - "leb128", -] - [[package]] name = "wasm-encoder" version = "0.16.0" @@ -1655,30 +1646,30 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.86.0" +version = "0.89.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcbfe95447da2aa7ff171857fc8427513eb57c75a729bb190e974dc695e8f5c" +checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" dependencies = [ "indexmap", ] [[package]] name = "wasmparser" -version = "0.89.1" +version = "0.90.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" +checksum = "b62c8d843f4423efee314dc75a1049886deba3214f7e7f9ff0e4e58b4d618581" dependencies = [ "indexmap", ] [[package]] name = "wasmprinter" -version = "0.2.36" +version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4cca415278da771add7c9ab7f3391f04b8d98719d2cf28a185d38d5206697e" +checksum = "aa9e5ee2f56cc8a5da489558114e8c118e5a8416d96aefe63dcf1b5b05b858c6" dependencies = [ "anyhow", - "wasmparser 0.86.0", + "wasmparser 0.89.1", ] [[package]] @@ -1909,7 +1900,7 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.16.0", + "wasm-encoder", ] [[package]] @@ -2213,8 +2204,8 @@ dependencies = [ "indexmap", "log", "pretty_assertions", - "wasm-encoder 0.13.0", - "wasmparser 0.86.0", + "wasm-encoder", + "wasmparser 0.90.0", "wasmprinter", "wat", "wit-parser", diff --git a/crates/bindgen-core/src/lib.rs b/crates/bindgen-core/src/lib.rs index b2f90b335..ef15ccca4 100644 --- a/crates/bindgen-core/src/lib.rs +++ b/crates/bindgen-core/src/lib.rs @@ -204,7 +204,9 @@ impl Types { for (_, ty) in f.params.iter() { self.set_param_result_ty(iface, ty, true, false); } - self.set_param_result_ty(iface, &f.result, false, true); + for ty in f.results.iter_types() { + self.set_param_result_ty(iface, ty, false, true); + } } } @@ -232,7 +234,7 @@ impl Types { TypeDefKind::Enum(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { - info |= self.type_info(iface, &case.ty); + info |= self.optional_type_info(iface, case.ty.as_ref()); } } TypeDefKind::List(ty) => { @@ -246,8 +248,8 @@ impl Types { info = self.type_info(iface, ty); } TypeDefKind::Result(r) => { - info = self.type_info(iface, &r.ok); - info |= self.type_info(iface, &r.err); + info = self.optional_type_info(iface, r.ok.as_ref()); + info |= self.optional_type_info(iface, r.err.as_ref()); } TypeDefKind::Union(u) => { for case in u.cases.iter() { @@ -255,11 +257,11 @@ impl Types { } } TypeDefKind::Future(ty) => { - info = self.type_info(iface, ty); + info = self.optional_type_info(iface, ty.as_ref()); } TypeDefKind::Stream(stream) => { - info = self.type_info(iface, &stream.element); - info |= self.type_info(iface, &stream.end); + info = self.optional_type_info(iface, stream.element.as_ref()); + info |= self.optional_type_info(iface, stream.end.as_ref()); } } self.type_info.insert(ty, info); @@ -277,6 +279,13 @@ impl Types { info } + fn optional_type_info(&mut self, iface: &Interface, ty: Option<&Type>) -> TypeInfo { + match ty { + Some(ty) => self.type_info(iface, ty), + None => TypeInfo::default(), + } + } + fn set_param_result_id(&mut self, iface: &Interface, ty: TypeId, param: bool, result: bool) { match &iface.types[ty].kind { TypeDefKind::Record(r) => { @@ -293,25 +302,27 @@ impl Types { TypeDefKind::Enum(_) => {} TypeDefKind::Variant(v) => { for case in v.cases.iter() { - self.set_param_result_ty(iface, &case.ty, param, result) + self.set_param_result_optional_ty(iface, case.ty.as_ref(), param, result) } } TypeDefKind::List(ty) | TypeDefKind::Type(ty) | TypeDefKind::Option(ty) => { self.set_param_result_ty(iface, ty, param, result) } TypeDefKind::Result(r) => { - self.set_param_result_ty(iface, &r.ok, param, result); - self.set_param_result_ty(iface, &r.err, param, result); + self.set_param_result_optional_ty(iface, r.ok.as_ref(), param, result); + self.set_param_result_optional_ty(iface, r.err.as_ref(), param, result); } TypeDefKind::Union(u) => { for case in u.cases.iter() { self.set_param_result_ty(iface, &case.ty, param, result) } } - TypeDefKind::Future(ty) => self.set_param_result_ty(iface, ty, param, result), + TypeDefKind::Future(ty) => { + self.set_param_result_optional_ty(iface, ty.as_ref(), param, result) + } TypeDefKind::Stream(stream) => { - self.set_param_result_ty(iface, &stream.element, param, result); - self.set_param_result_ty(iface, &stream.end, param, result); + self.set_param_result_optional_ty(iface, stream.element.as_ref(), param, result); + self.set_param_result_optional_ty(iface, stream.end.as_ref(), param, result); } } } @@ -330,6 +341,19 @@ impl Types { _ => {} } } + + fn set_param_result_optional_ty( + &mut self, + iface: &Interface, + ty: Option<&Type>, + param: bool, + result: bool, + ) { + match ty { + Some(ty) => self.set_param_result_ty(iface, ty, param, result), + None => (), + } + } } #[derive(Default)] diff --git a/crates/gen-guest-c/src/lib.rs b/crates/gen-guest-c/src/lib.rs index 8b407c8ae..2177c22da 100644 --- a/crates/gen-guest-c/src/lib.rs +++ b/crates/gen-guest-c/src/lib.rs @@ -56,7 +56,7 @@ impl Opts { #[derive(Debug)] struct Return { - splat_tuple: bool, + return_multiple: bool, scalar: Option, retptrs: Vec, } @@ -92,11 +92,21 @@ impl C { fn classify_ret(&mut self, iface: &Interface, func: &Function) -> Return { let mut ret = Return { - splat_tuple: false, + return_multiple: false, scalar: None, retptrs: Vec::new(), }; - ret.return_single(iface, &func.result, &func.result); + match func.results.len() { + 0 => ret.scalar = Some(Scalar::Void), + 1 => { + let ty = func.results.iter_types().next().unwrap(); + ret.return_single(iface, ty, ty); + } + _ => { + ret.return_multiple = true; + ret.retptrs.extend(func.results.iter_types().cloned()); + } + } return ret; } @@ -216,7 +226,6 @@ impl C { fn print_ty(&mut self, iface: &Interface, ty: &Type) { match ty { - Type::Unit => self.src.h("void"), Type::Bool => self.src.h("bool"), Type::Char => self.src.h("uint32_t"), // TODO: better type? Type::U8 => self.src.h("uint8_t"), @@ -264,7 +273,6 @@ impl C { fn print_ty_name(&mut self, iface: &Interface, ty: &Type) { match ty { - Type::Unit => self.src.h("unit"), Type::Bool => self.src.h("bool"), Type::Char => self.src.h("char32"), Type::U8 => self.src.h("u8"), @@ -307,9 +315,9 @@ impl C { } TypeDefKind::Result(r) => { self.src.h("result_"); - self.print_ty_name(iface, &r.ok); + self.print_optional_ty_name(iface, r.ok.as_ref()); self.src.h("_"); - self.print_ty_name(iface, &r.err); + self.print_optional_ty_name(iface, r.err.as_ref()); } TypeDefKind::List(t) => { self.src.h("list_"); @@ -317,19 +325,26 @@ impl C { } TypeDefKind::Future(t) => { self.src.h("future_"); - self.print_ty_name(iface, t); + self.print_optional_ty_name(iface, t.as_ref()); } TypeDefKind::Stream(s) => { self.src.h("stream_"); - self.print_ty_name(iface, &s.element); + self.print_optional_ty_name(iface, s.element.as_ref()); self.src.h("_"); - self.print_ty_name(iface, &s.end); + self.print_optional_ty_name(iface, s.end.as_ref()); } } } } } + fn print_optional_ty_name(&mut self, iface: &Interface, ty: Option<&Type>) { + match ty { + Some(ty) => self.print_ty_name(iface, ty), + None => self.src.h("void"), + } + } + fn print_anonymous_type(&mut self, iface: &Interface, ty: TypeId) { let prev = mem::take(&mut self.src.h); self.src.h("typedef "); @@ -365,12 +380,12 @@ impl C { bool is_err; union { "); - if !self.is_empty_type(iface, &r.ok) { - self.print_ty(iface, &r.ok); + if let Some(ok) = self.get_nonempty_type(iface, r.ok.as_ref()) { + self.print_ty(iface, ok); self.src.h(" ok;\n"); } - if !self.is_empty_type(iface, &r.err) { - self.print_ty(iface, &r.err); + if let Some(err) = self.get_nonempty_type(iface, r.err.as_ref()) { + self.print_ty(iface, err); self.src.h(" err;\n"); } self.src.h("} val;\n"); @@ -396,7 +411,6 @@ impl C { fn is_empty_type(&self, iface: &Interface, ty: &Type) -> bool { let id = match ty { Type::Id(id) => *id, - Type::Unit => return true, _ => return false, }; match &iface.types[id].kind { @@ -407,6 +421,19 @@ impl C { } } + fn get_nonempty_type<'o>(&self, iface: &Interface, ty: Option<&'o Type>) -> Option<&'o Type> { + match ty { + Some(ty) => { + if self.is_empty_type(iface, ty) { + None + } else { + Some(ty) + } + } + None => None, + } + } + fn print_intrinsics(&mut self) { // Note that these intrinsics are declared as `weak` so they can be // overridden from some other symbol. @@ -502,14 +529,18 @@ impl C { TypeDefKind::Variant(v) => { self.src.c("switch ((int32_t) ptr->tag) {\n"); for (i, case) in v.cases.iter().enumerate() { - if !self.owns_anything(iface, &case.ty) { - continue; + if let Some(ty) = &case.ty { + if !self.owns_anything(iface, ty) { + continue; + } + uwriteln!(self.src.c, "case {}: {{", i); + let expr = format!("&ptr->val.{}", case.name.to_snake_case()); + if let Some(ty) = &case.ty { + self.free(iface, ty, &expr); + } + self.src.c("break;\n"); + self.src.c("}\n"); } - uwriteln!(self.src.c, "case {}: {{", i); - let expr = format!("&ptr->val.{}", case.name.to_snake_case()); - self.free(iface, &case.ty, &expr); - self.src.c("break;\n"); - self.src.c("}\n"); } self.src.c("}\n"); } @@ -537,12 +568,16 @@ impl C { TypeDefKind::Result(r) => { self.src.c("if (!ptr->is_err) {\n"); - if self.owns_anything(iface, &r.ok) { - self.free(iface, &r.ok, "&ptr->val.ok"); + if let Some(ok) = &r.ok { + if self.owns_anything(iface, ok) { + self.free(iface, ok, "&ptr->val.ok"); + } } - if self.owns_anything(iface, &r.err) { - self.src.c("} else {\n"); - self.free(iface, &r.err, "&ptr->val.err"); + if let Some(err) = &r.err { + if self.owns_anything(iface, err) { + self.src.c("} else {\n"); + self.free(iface, err, "&ptr->val.err"); + } } self.src.c("}\n"); } @@ -566,20 +601,31 @@ impl C { TypeDefKind::Flags(_) => false, TypeDefKind::Enum(_) => false, TypeDefKind::List(_) => true, - TypeDefKind::Variant(v) => v.cases.iter().any(|c| self.owns_anything(iface, &c.ty)), + TypeDefKind::Variant(v) => v + .cases + .iter() + .any(|c| self.optional_owns_anything(iface, c.ty.as_ref())), TypeDefKind::Union(v) => v .cases .iter() .any(|case| self.owns_anything(iface, &case.ty)), TypeDefKind::Option(t) => self.owns_anything(iface, t), TypeDefKind::Result(r) => { - self.owns_anything(iface, &r.ok) || self.owns_anything(iface, &r.err) + self.optional_owns_anything(iface, r.ok.as_ref()) + || self.optional_owns_anything(iface, r.err.as_ref()) } TypeDefKind::Future(_) => todo!("owns_anything for future"), TypeDefKind::Stream(_) => todo!("owns_anything for stream"), } } + fn optional_owns_anything(&self, iface: &Interface, ty: Option<&Type>) -> bool { + match ty { + Some(ty) => self.owns_anything(iface, ty), + None => false, + } + } + fn free(&mut self, iface: &Interface, ty: &Type, expr: &str) { let prev = mem::take(&mut self.src.h); self.print_namespace(iface); @@ -613,37 +659,18 @@ impl Return { self.retptrs.push(*orig_ty); return; } - Type::Unit => { - self.scalar = Some(Scalar::Void); - return; - } _ => { self.scalar = Some(Scalar::Type(*orig_ty)); return; } }; match &iface.types[id].kind { - TypeDefKind::Type(t) => self.return_single(iface, t, orig_ty), + TypeDefKind::Type(t) => return self.return_single(iface, t, orig_ty), - // Flags are returned as their bare values - TypeDefKind::Flags(_) => { - self.scalar = Some(Scalar::Type(*orig_ty)); - } - - // Tuples get splatted to multiple return pointers - TypeDefKind::Tuple(_) => self.splat_tuples(iface, ty, orig_ty), - - // Record returns are always through a pointer - TypeDefKind::Record(_) => { - self.retptrs.push(*orig_ty); - } - - // other records/lists/buffers always go to return pointers - TypeDefKind::List(_) => self.retptrs.push(*orig_ty), - - // Enums are scalars - TypeDefKind::Enum(_) => { + // Flags are returned as their bare values, and enums are scalars + TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => { self.scalar = Some(Scalar::Type(*orig_ty)); + return; } // Unpack optional returns where a boolean discriminant is @@ -652,52 +679,41 @@ impl Return { TypeDefKind::Option(ty) => { self.scalar = Some(Scalar::OptionBool(*ty)); self.retptrs.push(*ty); + return; } // Unpack `result` returns where `E` looks like an enum // so we can return that in the scalar return and have `T` get // returned through the normal returns. TypeDefKind::Result(r) => { - if let Type::Id(err) = r.err { + if let Some(Type::Id(err)) = r.err { if let TypeDefKind::Enum(enum_) = &iface.types[err].kind { self.scalar = Some(Scalar::ResultEnum { err, max_err: enum_.cases.len(), }); - self.splat_tuples(iface, &r.ok, &r.ok); + if let Some(ok) = r.ok { + self.retptrs.push(ok); + } return; } } - // otherwise just return the variant via a normal - // return pointer - self.retptrs.push(*orig_ty); + // fall through to the return pointer case } - TypeDefKind::Variant(_) | TypeDefKind::Union(_) => { - self.retptrs.push(*orig_ty); - } + // These types are always returned indirectly. + TypeDefKind::Tuple(_) + | TypeDefKind::Record(_) + | TypeDefKind::List(_) + | TypeDefKind::Variant(_) + | TypeDefKind::Union(_) => {} + TypeDefKind::Future(_) => todo!("return_single for future"), TypeDefKind::Stream(_) => todo!("return_single for stream"), } - } - fn splat_tuples(&mut self, iface: &Interface, ty: &Type, orig_ty: &Type) { - let id = match ty { - Type::Id(id) => *id, - Type::Unit => return, - _ => { - self.retptrs.push(*orig_ty); - return; - } - }; - match &iface.types[id].kind { - TypeDefKind::Tuple(t) => { - self.splat_tuple = true; - self.retptrs.extend(t.types.iter()); - } - _ => self.retptrs.push(*orig_ty), - } + self.retptrs.push(*orig_ty); } } @@ -807,13 +823,12 @@ impl Generator for C { self.src.h(" tag;\n"); self.src.h("union {\n"); for case in variant.cases.iter() { - if self.is_empty_type(iface, &case.ty) { - continue; + if let Some(ty) = self.get_nonempty_type(iface, case.ty.as_ref()) { + self.print_ty(iface, ty); + self.src.h(" "); + self.src.h(&case.name.to_snake_case()); + self.src.h(";\n"); } - self.print_ty(iface, &case.ty); - self.src.h(" "); - self.src.h(&case.name.to_snake_case()); - self.src.h(";\n"); } self.src.h("} val;\n"); self.src.h("} "); @@ -901,12 +916,12 @@ impl Generator for C { self.src.h("typedef struct {\n"); self.src.h("bool is_err;\n"); self.src.h("union {\n"); - if !self.is_empty_type(iface, &result.ok) { - self.print_ty(iface, &result.ok); + if let Some(ok) = self.get_nonempty_type(iface, result.ok.as_ref()) { + self.print_ty(iface, ok); self.src.h(" ok;\n"); } - if !self.is_empty_type(iface, &result.err) { - self.print_ty(iface, &result.err); + if let Some(err) = self.get_nonempty_type(iface, result.err.as_ref()) { + self.print_ty(iface, err); self.src.h(" err;\n"); } self.src.h("} val;\n"); @@ -1402,18 +1417,9 @@ impl<'a> FunctionBindgen<'a> { } fn store_in_retptrs(&mut self, operands: &[String]) { - if self.sig.ret.splat_tuple { - assert_eq!(operands.len(), 1); - let op = &operands[0]; - for (i, ptr) in self.sig.retptrs.clone().into_iter().enumerate() { - self.store_op(&format!("{}.f{}", op, i), &format!("*{}", ptr)); - } - // ... - } else { - assert_eq!(operands.len(), self.sig.retptrs.len()); - for (op, ptr) in operands.iter().zip(self.sig.retptrs.clone()) { - self.store_op(op, &format!("*{}", ptr)); - } + assert_eq!(operands.len(), self.sig.retptrs.len()); + for (op, ptr) in operands.iter().zip(self.sig.retptrs.clone()) { + self.store_op(op, &format!("*{}", ptr)); } } } @@ -1537,10 +1543,6 @@ impl Bindgen for FunctionBindgen<'_> { } } - Instruction::UnitLower => {} - Instruction::UnitLift => { - results.push("INVALID".to_string()); - } Instruction::BoolFromI32 | Instruction::I32FromBool => { results.push(operands[0].clone()); } @@ -1655,8 +1657,8 @@ impl Bindgen for FunctionBindgen<'_> { variant.cases.iter().zip(blocks).zip(payloads).enumerate() { uwriteln!(self.src, "case {}: {{", i); - if !self.gen.is_empty_type(iface, &case.ty) { - let ty = self.gen.type_string(iface, &case.ty); + if let Some(ty) = self.gen.get_nonempty_type(iface, case.ty.as_ref()) { + let ty = self.gen.type_string(iface, ty); uwrite!( self.src, "const {} *{} = &({}).val", @@ -1694,9 +1696,9 @@ impl Bindgen for FunctionBindgen<'_> { { uwriteln!(self.src, "case {}: {{", i); self.src.push_str(&block); - assert!(block_results.len() == 1); + assert!(block_results.len() == (case.ty.is_some() as usize)); - if !self.gen.is_empty_type(iface, &case.ty) { + if let Some(_) = self.gen.get_nonempty_type(iface, case.ty.as_ref()) { let mut dst = format!("{}.val", result); dst.push_str("."); dst.push_str(&case.name.to_snake_case()); @@ -1823,10 +1825,9 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::OptionLift { payload, ty, .. } => { let (some, some_results) = self.blocks.pop().unwrap(); let (none, none_results) = self.blocks.pop().unwrap(); - assert!(none_results.len() == 1); + assert!(none_results.len() == 0); assert!(some_results.len() == 1); let some_result = &some_results[0]; - assert_eq!(none_results[0], "INVALID"); let ty = self.gen.type_string(iface, &Type::Id(*ty)); let result = self.locals.tmp("option"); @@ -1880,18 +1881,20 @@ impl Bindgen for FunctionBindgen<'_> { } let op0 = &operands[0]; - let ok_ty = self.gen.type_string(iface, &result.ok); - let err_ty = self.gen.type_string(iface, &result.err); - let bind_ok = if self.gen.is_empty_type(iface, &result.ok) { - String::new() - } else { - format!("const {ok_ty} *{ok_payload} = &({op0}).val.ok;") - }; - let bind_err = if self.gen.is_empty_type(iface, &result.err) { - String::new() - } else { - format!("const {err_ty} *{err_payload} = &({op0}).val.err;") - }; + let bind_ok = + if let Some(ok) = self.gen.get_nonempty_type(iface, result.ok.as_ref()) { + let ok_ty = self.gen.type_string(iface, ok); + format!("const {ok_ty} *{ok_payload} = &({op0}).val.ok;") + } else { + String::new() + }; + let bind_err = + if let Some(err) = self.gen.get_nonempty_type(iface, result.err.as_ref()) { + let err_ty = self.gen.type_string(iface, err); + format!("const {err_ty} *{err_payload} = &({op0}).val.err;") + } else { + String::new() + }; uwrite!( self.src, " @@ -1908,23 +1911,25 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::ResultLift { result, ty, .. } => { let (err, err_results) = self.blocks.pop().unwrap(); - assert!(err_results.len() == 1); - let err_result = &err_results[0]; + assert!(err_results.len() == (result.err.is_some() as usize)); let (ok, ok_results) = self.blocks.pop().unwrap(); - assert!(ok_results.len() == 1); - let ok_result = &ok_results[0]; + assert!(ok_results.len() == (result.ok.is_some() as usize)); let result_tmp = self.locals.tmp("result"); - let set_ok = if self.gen.is_empty_type(iface, &result.ok) { - String::new() - } else { + let set_ok = if let Some(_) = self.gen.get_nonempty_type(iface, result.ok.as_ref()) + { + let ok_result = &ok_results[0]; format!("{result_tmp}.val.ok = {ok_result};") - }; - let set_err = if self.gen.is_empty_type(iface, &result.err) { - String::new() } else { - format!("{result_tmp}.val.err = {err_result};") + String::new() }; + let set_err = + if let Some(_) = self.gen.get_nonempty_type(iface, result.err.as_ref()) { + let err_result = &err_results[0]; + format!("{result_tmp}.val.err = {err_result};") + } else { + String::new() + }; let ty = self.gen.type_string(iface, &Type::Id(*ty)); uwriteln!(self.src, "{ty} {result_tmp};"); @@ -2044,20 +2049,16 @@ impl Bindgen for FunctionBindgen<'_> { retptrs.push(name); } uwriteln!(self.src, "{}({});", self.sig.name, args); - if self.sig.ret.splat_tuple { - let ty = self.gen.type_string(iface, &func.result); - results.push(format!("({}){{ {} }}", ty, retptrs.join(", "))); - } else if self.sig.retptrs.len() > 0 { - results.extend(retptrs); - } + results.extend(retptrs); } Some(Scalar::Void) => { uwriteln!(self.src, "{}({});", self.sig.name, args); - results.push("INVALID".to_string()); } Some(Scalar::Type(_)) => { let ret = self.locals.tmp("ret"); - let ty = self.gen.type_string(iface, &func.result); + let ty = self + .gen + .type_string(iface, func.results.iter_types().next().unwrap()); uwriteln!(self.src, "{} {} = {}({});", ty, ret, self.sig.name, args); results.push(ret); } @@ -2072,7 +2073,9 @@ impl Bindgen for FunctionBindgen<'_> { let payload_ty = self.gen.type_string(iface, ty); uwriteln!(self.src, "{} {};", payload_ty, val); uwriteln!(self.src, "bool {} = {}({});", ret, self.sig.name, args); - let option_ty = self.gen.type_string(iface, &func.result); + let option_ty = self + .gen + .type_string(iface, func.results.iter_types().next().unwrap()); let option_ret = self.locals.tmp("ret"); uwrite!( self.src, @@ -2111,7 +2114,9 @@ impl Bindgen for FunctionBindgen<'_> { self.sig.name, args, ); - let result_ty = self.gen.type_string(iface, &func.result); + let result_ty = self + .gen + .type_string(iface, func.results.iter_types().next().unwrap()); let result_ret = self.locals.tmp("ret"); uwrite!( self.src, @@ -2131,12 +2136,6 @@ impl Bindgen for FunctionBindgen<'_> { max = max_err, set_ok = if self.sig.ret.retptrs.len() == 0 { String::new() - } else if self.sig.ret.splat_tuple { - let mut s = String::new(); - for (i, name) in ok_names.iter().enumerate() { - uwriteln!(s, "{}.val.ok.f{} = {};", result_ret, i, name,); - } - s } else { let name = ok_names.pop().unwrap(); format!("{}.val.ok = {};", result_ret, name) @@ -2149,7 +2148,7 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::Return { .. } if self.gen.in_import => match self.sig.ret.scalar { None => self.store_in_retptrs(operands), Some(Scalar::Void) => { - assert_eq!(operands, &["INVALID"]); + assert!(operands.is_empty()); } Some(Scalar::Type(_)) => { assert_eq!(operands.len(), 1); diff --git a/crates/gen-guest-c/tests/codegen.rs b/crates/gen-guest-c/tests/codegen.rs index 3b3d89326..bc7e851e9 100644 --- a/crates/gen-guest-c/tests/codegen.rs +++ b/crates/gen-guest-c/tests/codegen.rs @@ -4,7 +4,6 @@ use std::process::Command; mod imports { test_helpers::codegen_c_import!( - // ... "*.wit" // TODO: implement async support diff --git a/crates/gen-guest-rust/src/lib.rs b/crates/gen-guest-rust/src/lib.rs index 3a3869941..e07b8d1e5 100644 --- a/crates/gen-guest-rust/src/lib.rs +++ b/crates/gen-guest-rust/src/lib.rs @@ -950,13 +950,6 @@ impl Bindgen for FunctionBindgen<'_> { wit_bindgen_gen_rust_lib::bitcast(casts, operands, results) } - Instruction::UnitLower => { - self.push_str(&format!("let () = {};\n", operands[0])); - } - Instruction::UnitLift => { - results.push("()".to_string()); - } - Instruction::I32FromBool => { results.push(format!("match {} {{ true => 1, false => 0 }}", operands[0])); } @@ -1059,10 +1052,10 @@ impl Bindgen for FunctionBindgen<'_> { for (case, block) in variant.cases.iter().zip(blocks) { let case_name = case.name.to_camel_case(); self.push_str(&format!("{name}::{case_name}")); - if case.ty == Type::Unit { - self.push_str(&format!(" => {{\nlet e = ();\n{block}\n}}\n")); - } else { + if case.ty.is_some() { self.push_str(&format!("(e) => {block},\n")); + } else { + self.push_str(&format!(" => {{\n{block}\n}}\n")); } } self.push_str("};\n"); @@ -1071,7 +1064,7 @@ impl Bindgen for FunctionBindgen<'_> { // 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::VariantLift { name, variant, .. } - if variant.cases.iter().all(|c| c.ty == Type::Unit) && unchecked => + if variant.cases.iter().all(|c| c.ty.is_none()) && unchecked => { self.blocks.drain(self.blocks.len() - variant.cases.len()..); let mut result = format!("core::mem::transmute::<_, "); @@ -1098,7 +1091,7 @@ impl Bindgen for FunctionBindgen<'_> { } else { i.to_string() }; - let block = if case.ty != Type::Unit { + let block = if case.ty.is_some() { format!("({block})") } else { String::new() @@ -1178,7 +1171,7 @@ impl Bindgen for FunctionBindgen<'_> { self.push_str(&format!( "match {operand} {{ Some(e) => {some}, - None => {{\nlet e = ();\n{none}\n}}, + None => {{\n{none}\n}}, }};" )); } @@ -1204,16 +1197,19 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::ResultLower { results: result_types, + result, .. } => { let err = self.blocks.pop().unwrap(); let ok = self.blocks.pop().unwrap(); self.let_results(result_types.len(), results); let operand = &operands[0]; + let ok_binding = if result.ok.is_some() { "e" } else { "_" }; + let err_binding = if result.err.is_some() { "e" } else { "_" }; self.push_str(&format!( "match {operand} {{ - Ok(e) => {{ {ok} }}, - Err(e) => {{ {err} }}, + Ok({ok_binding}) => {{ {ok} }}, + Err({err_binding}) => {{ {err} }}, }};" )); } @@ -1453,8 +1449,7 @@ impl Bindgen for FunctionBindgen<'_> { } Instruction::CallInterface { module, func } => { - self.push_str("let result = "); - results.push("result".to_string()); + self.let_results(func.results.len(), results); match &func.kind { FunctionKind::Freestanding => { if self.gen.opts.standalone { diff --git a/crates/gen-host-js/src/lib.rs b/crates/gen-host-js/src/lib.rs index 8fabb8946..1dbbf4faa 100644 --- a/crates/gen-host-js/src/lib.rs +++ b/crates/gen-host-js/src/lib.rs @@ -141,7 +141,7 @@ impl Js { fn array_ty(&self, iface: &Interface, ty: &Type) -> Option<&'static str> { match ty { - Type::Unit | Type::Bool => None, + Type::Bool => None, Type::U8 => Some("Uint8Array"), Type::S8 => Some("Int8Array"), Type::U16 => Some("Uint16Array"), @@ -164,7 +164,6 @@ impl Js { fn print_ty(&mut self, iface: &Interface, ty: &Type) { match ty { - Type::Unit => self.src.ts("void"), Type::Bool => self.src.ts("boolean"), Type::U8 | Type::S8 @@ -204,9 +203,9 @@ impl Js { TypeDefKind::Result(r) => { self.needs_ty_result = true; self.src.ts("Result<"); - self.print_ty(iface, &r.ok); + self.print_optional_ty(iface, r.ok.as_ref()); self.src.ts(", "); - self.print_ty(iface, &r.err); + self.print_optional_ty(iface, r.err.as_ref()); self.src.ts(">"); } TypeDefKind::Variant(_) => panic!("anonymous variant"), @@ -218,6 +217,13 @@ impl Js { } } + fn print_optional_ty(&mut self, iface: &Interface, ty: Option<&Type>) { + match ty { + Some(ty) => self.print_ty(iface, ty), + None => self.src.ts("void"), + } + } + fn print_list(&mut self, iface: &Interface, ty: &Type) { match self.array_ty(iface, ty) { Some(ty) => self.src.ts(ty), @@ -301,7 +307,20 @@ impl Js { self.print_ty(iface, ty); } self.src.ts("): "); - self.print_ty(iface, &func.result); + match func.results.len() { + 0 => self.src.ts("void"), + 1 => self.print_ty(iface, func.results.iter_types().next().unwrap()), + _ => { + self.src.ts("["); + for (i, ty) in func.results.iter_types().enumerate() { + if i != 0 { + self.src.ts(", "); + } + self.print_ty(iface, ty); + } + self.src.ts("]"); + } + } self.src.ts(";\n"); } @@ -451,9 +470,9 @@ impl Generator for Js { self.src.ts("tag: \""); self.src.ts(&case.name); self.src.ts("\",\n"); - if case.ty != Type::Unit { + if let Some(ty) = case.ty { self.src.ts("val: "); - self.print_ty(iface, &case.ty); + self.print_ty(iface, &ty); self.src.ts(",\n"); } self.src.ts("}\n"); @@ -524,9 +543,9 @@ impl Generator for Js { let name = name.to_camel_case(); self.needs_ty_result = true; self.src.ts(&format!("export type {name} = Result<")); - self.print_ty(iface, &result.ok); + self.print_optional_ty(iface, result.ok.as_ref()); self.src.ts(", "); - self.print_ty(iface, &result.err); + self.print_optional_ty(iface, result.err.as_ref()); self.src.ts(">;\n"); } @@ -612,7 +631,7 @@ impl Generator for Js { .js(&format!("function({}) {{\n", params.join(", "))); self.ts_func(iface, func); - let mut f = FunctionBindgen::new(self, false, params); + let mut f = FunctionBindgen::new(self, params); iface.call( AbiVariant::GuestImport, LiftLower::LiftArgsLowerResults, @@ -704,7 +723,7 @@ impl Generator for Js { if !first_is_operand { params.remove(0); } - let mut f = FunctionBindgen::new(self, false, params); + let mut f = FunctionBindgen::new(self, params); f.src_object = src_object; iface.call( AbiVariant::GuestExport, @@ -1148,7 +1167,6 @@ struct FunctionBindgen<'a> { src: Source, block_storage: Vec, blocks: Vec<(String, Vec)>, - in_import: bool, needs_memory: bool, needs_realloc: Option, needs_free: Option, @@ -1157,14 +1175,13 @@ struct FunctionBindgen<'a> { } impl FunctionBindgen<'_> { - fn new(gen: &mut Js, in_import: bool, params: Vec) -> FunctionBindgen<'_> { + fn new(gen: &mut Js, params: Vec) -> FunctionBindgen<'_> { FunctionBindgen { gen, tmp: 0, src: Source::default(), block_storage: Vec::new(), blocks: Vec::new(), - in_import, needs_memory: false, needs_realloc: None, needs_free: None, @@ -1391,11 +1408,6 @@ impl Bindgen for FunctionBindgen<'_> { } } - Instruction::UnitLower => {} - Instruction::UnitLift => { - results.push("undefined".to_string()); - } - Instruction::BoolFromI32 => { let tmp = self.tmp(); self.src @@ -1613,7 +1625,7 @@ impl Bindgen for FunctionBindgen<'_> { for (case, (block, block_results)) in variant.cases.iter().zip(blocks) { self.src .js(&format!("case \"{}\": {{\n", case.name.as_str())); - if case.ty != Type::Unit { + if case.ty.is_some() { self.src.js(&format!("const e = variant{}.val;\n", tmp)); } self.src.js(&block); @@ -1651,11 +1663,11 @@ impl Bindgen for FunctionBindgen<'_> { self.src.js(&format!("variant{} = {{\n", tmp)); self.src.js(&format!("tag: \"{}\",\n", case.name.as_str())); - assert!(block_results.len() == 1); - if case.ty != Type::Unit { + if case.ty.is_some() { + assert!(block_results.len() == 1); self.src.js(&format!("val: {},\n", block_results[0])); } else { - assert_eq!(block_results[0], "undefined"); + assert!(block_results.len() == 0); } self.src.js("};\n"); self.src.js("break;\n}\n"); @@ -1807,10 +1819,9 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::OptionLift { payload, .. } => { let (some, some_results) = self.blocks.pop().unwrap(); let (none, none_results) = self.blocks.pop().unwrap(); - assert!(none_results.len() == 1); + assert!(none_results.len() == 0); assert!(some_results.len() == 1); let some_result = &some_results[0]; - assert_eq!(none_results[0], "undefined"); let tmp = self.tmp(); @@ -1898,11 +1909,23 @@ impl Bindgen for FunctionBindgen<'_> { )); } - Instruction::ResultLift { .. } => { + Instruction::ResultLift { result, .. } => { let (err, err_results) = self.blocks.pop().unwrap(); let (ok, ok_results) = self.blocks.pop().unwrap(); - let err_result = &err_results[0]; - let ok_result = &ok_results[0]; + let ok_result = if result.ok.is_some() { + assert_eq!(ok_results.len(), 1); + format!("{}", ok_results[0]) + } else { + assert_eq!(ok_results.len(), 0); + String::from("undefined") + }; + let err_result = if result.err.is_some() { + assert_eq!(err_results.len(), 1); + format!("{}", err_results[0]) + } else { + assert_eq!(err_results.len(), 0); + String::from("undefined") + }; let tmp = self.tmp(); let op0 = &operands[0]; self.src.js(&format!( @@ -2193,14 +2216,27 @@ impl Bindgen for FunctionBindgen<'_> { )); } }; - let mut bind_results = |me: &mut FunctionBindgen<'_>| match &func.result { - Type::Unit => { - results.push("".to_string()); + let mut bind_results = |me: &mut FunctionBindgen<'_>| { + let amt = func.results.len(); + if amt == 0 { + return; + } + me.src.js("const "); + if amt > 1 { + me.src.js("["); } - _ => { - me.src.js("const ret = "); - results.push("ret".to_string()); + for i in 0..amt { + if i > 0 { + me.src.js(", "); + } + let name = format!("ret{i}"); + me.src.js(&name); + results.push(name); } + if amt > 1 { + me.src.js("]"); + } + me.src.js(" = "); }; bind_results(self); @@ -2211,10 +2247,7 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::Return { amt, func: _ } => match amt { 0 => {} 1 => self.src.js(&format!("return {};\n", operands[0])), - _ => { - assert!(self.in_import); - self.src.js(&format!("return [{}];\n", operands.join(", "))); - } + _ => self.src.js(&format!("return [{}];\n", operands.join(", "))), }, Instruction::I32Load { offset } => self.load("getInt32", *offset, operands, results), diff --git a/crates/gen-host-wasmtime-py/src/lib.rs b/crates/gen-host-wasmtime-py/src/lib.rs index 1f95bf0e2..3b314ea32 100644 --- a/crates/gen-host-wasmtime-py/src/lib.rs +++ b/crates/gen-host-wasmtime-py/src/lib.rs @@ -107,7 +107,7 @@ impl WasmtimePy { fn array_ty(iface: &Interface, ty: &Type) -> Option<&'static str> { match ty { - Type::Unit | Type::Bool => None, + Type::Bool => None, Type::U8 => Some("c_uint8"), Type::S8 => Some("c_int8"), Type::U16 => Some("c_uint16"), @@ -221,8 +221,13 @@ impl Generator for WasmtimePy { let case_name = format!("{}{}", name.to_camel_case(), case.name.to_camel_case()); builder.push_str(&format!("class {case_name}:\n")); builder.indent(); - builder.push_str("value: "); - builder.print_ty(&case.ty, true); + match &case.ty { + Some(ty) => { + builder.push_str("value: "); + builder.print_ty(ty, true); + } + None => builder.push_str("pass"), + } builder.push_str("\n"); builder.dedent(); builder.push_str("\n"); @@ -298,9 +303,9 @@ impl Generator for WasmtimePy { let mut builder = self.src.builder(&mut self.deps, iface); builder.comment(docs); builder.push_str(&format!("{} = Result[", name.to_camel_case())); - builder.print_ty(&result.ok, true); + builder.print_optional_ty(result.ok.as_ref(), true); builder.push_str(", "); - builder.print_ty(&result.err, true); + builder.print_optional_ty(result.err.as_ref(), true); builder.push_str("]\n\n"); } @@ -1077,10 +1082,6 @@ impl Bindgen for FunctionBindgen<'_> { } } - Instruction::UnitLower => {} - Instruction::UnitLift => { - results.push("None".to_string()); - } Instruction::BoolFromI32 => { let op = self.locals.tmp("operand"); let ret = self.locals.tmp("boolean"); @@ -1246,7 +1247,9 @@ impl Bindgen for FunctionBindgen<'_> { case.name.to_camel_case() )); builder.indent(); - builder.push_str(&format!("{} = {}.value\n", payload, operands[0])); + if case.ty.is_some() { + builder.push_str(&format!("{} = {}.value\n", payload, operands[0])); + } builder.push_str(&block); for (i, result) in block_results.iter().enumerate() { @@ -1292,8 +1295,10 @@ impl Bindgen for FunctionBindgen<'_> { name.to_camel_case(), case.name.to_camel_case() )); - assert!(block_results.len() == 1); - builder.push_str(&block_results[0]); + if block_results.len() > 0 { + assert!(block_results.len() == 1); + builder.push_str(&block_results[0]); + } builder.push_str(")\n"); builder.dedent(); } @@ -1459,10 +1464,9 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::OptionLift { ty, .. } => { let (some, some_results) = self.blocks.pop().unwrap(); let (none, none_results) = self.blocks.pop().unwrap(); - assert!(none_results.len() == 1); + assert!(none_results.len() == 0); assert!(some_results.len() == 1); let some_result = &some_results[0]; - assert_eq!(none_results[0], "None"); let result = self.locals.tmp("option"); builder.print_var_declaration(&result, &Type::Id(*ty)); @@ -1529,10 +1533,9 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::ResultLift { ty, .. } => { let (err, err_results) = self.blocks.pop().unwrap(); let (ok, ok_results) = self.blocks.pop().unwrap(); - assert!(err_results.len() == 1); - let err_result = &err_results[0]; - assert!(ok_results.len() == 1); - let ok_result = &ok_results[0]; + let none = String::from("None"); + let err_result = err_results.get(0).unwrap_or(&none); + let ok_result = ok_results.get(0).unwrap_or(&none); let result = self.locals.tmp("expected"); builder.print_var_declaration(&result, &Type::Id(*ty)); @@ -1785,16 +1788,16 @@ impl Bindgen for FunctionBindgen<'_> { } } Instruction::CallInterface { module: _, func } => { - match &func.result { - Type::Unit => { - results.push("".to_string()); - } - _ => { - let result = self.locals.tmp("ret"); - builder.push_str(&result); - results.push(result); - builder.push_str(" = "); + for i in 0..func.results.len() { + if i > 0 { + builder.push_str(", "); } + let result = self.locals.tmp("ret"); + builder.push_str(&result); + results.push(result); + } + if func.results.len() > 0 { + builder.push_str(" = "); } match &func.kind { FunctionKind::Freestanding | FunctionKind::Static { .. } => { @@ -1863,7 +1866,6 @@ impl Bindgen for FunctionBindgen<'_> { fn py_type_class_of(ty: &Type) -> PyTypeClass { match ty { - Type::Unit => PyTypeClass::None, Type::Bool | Type::U8 | Type::U16 @@ -1881,7 +1883,6 @@ fn py_type_class_of(ty: &Type) -> PyTypeClass { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum PyTypeClass { - None, Int, Str, Float, diff --git a/crates/gen-host-wasmtime-py/src/source.rs b/crates/gen-host-wasmtime-py/src/source.rs index 7a4ff1594..007ea423c 100644 --- a/crates/gen-host-wasmtime-py/src/source.rs +++ b/crates/gen-host-wasmtime-py/src/source.rs @@ -173,7 +173,6 @@ impl<'s, 'd, 'i> SourceBuilder<'s, 'd, 'i> { /// on the root type only if `forward_ref` is true. pub fn print_ty(&mut self, ty: &Type, forward_ref: bool) { match ty { - Type::Unit => self.push_str("None"), Type::Bool => self.push_str("bool"), Type::U8 | Type::S8 @@ -221,22 +220,22 @@ impl<'s, 'd, 'i> SourceBuilder<'s, 'd, 'i> { TypeDefKind::Result(r) => { self.deps.needs_result = true; self.push_str("Result["); - self.print_ty(&r.ok, true); + self.print_optional_ty(r.ok.as_ref(), true); self.push_str(", "); - self.print_ty(&r.err, true); + self.print_optional_ty(r.err.as_ref(), true); self.push_str("]"); } TypeDefKind::List(t) => self.print_list(t), TypeDefKind::Future(t) => { self.push_str("Future["); - self.print_ty(t, true); + self.print_optional_ty(t.as_ref(), true); self.push_str("]"); } TypeDefKind::Stream(s) => { self.push_str("Stream["); - self.print_ty(&s.element, true); + self.print_optional_ty(s.element.as_ref(), true); self.push_str(", "); - self.print_ty(&s.end, true); + self.print_optional_ty(s.end.as_ref(), true); self.push_str("]"); } } @@ -244,6 +243,18 @@ impl<'s, 'd, 'i> SourceBuilder<'s, 'd, 'i> { } } + /// Appends an optional type's Python representation to this `Source`, or + /// Python `None` if the type is `None`. + /// Records any required intrinsics and imports in the `deps`. + /// Uses Python forward reference syntax (e.g. 'Foo') + /// on the root type only if `forward_ref` is true. + pub fn print_optional_ty(&mut self, ty: Option<&Type>, forward_ref: bool) { + match ty { + Some(ty) => self.print_ty(ty, forward_ref), + None => self.push_str("None"), + } + } + /// Appends a tuple type's Python representation to this `Source`. /// Records any required intrinsics and imports in the `deps`. /// Uses Python forward reference syntax (e.g. 'Foo') for named type parameters. @@ -262,6 +273,32 @@ impl<'s, 'd, 'i> SourceBuilder<'s, 'd, 'i> { self.push_str("]"); } + /// Appends (potentially multiple) return type's Python representation to this `Source`. + /// Records any required intrinsics and imports in the `deps`. + /// Uses Python forward reference syntax (e.g. 'Foo') for named type parameters. + pub fn print_return_types(&mut self, return_types: &Params) { + // Return None for empty wit return types. + if return_types.len() == 0 { + return self.push_str("None"); + } + // Return the type directly for a single wit return type (whether named or not). + if return_types.len() == 1 { + let ty = &return_types.iter().next().unwrap().1; + self.print_ty(ty, true); + } else { + // Return a tuple for multiple wit return types. + self.deps.pyimport("typing", "Tuple"); + self.push_str("Tuple["); + for (i, (_, t)) in return_types.iter().enumerate() { + if i > 0 { + self.push_str(", "); + } + self.print_ty(t, true); + } + self.push_str("]"); + } + } + /// Appends a Python type representing a sequence of the `element` type to this `Source`. /// If the element type is `Type::U8`, the result type is `bytes` otherwise it is a `List[T]` /// Records any required intrinsics and imports in the `deps`. @@ -325,7 +362,21 @@ impl<'s, 'd, 'i> SourceBuilder<'s, 'd, 'i> { self.print_ty(ty, true); } self.source.push_str(") -> "); - self.print_ty(&func.result, true); + match func.results.len() { + 0 => self.source.push_str("None"), + 1 => self.print_ty(func.results.iter_types().next().unwrap(), true), + _ => { + self.deps.pyimport("typing", "Tuple"); + self.push_str("Tuple["); + for (i, ty) in func.results.iter_types().enumerate() { + if i > 0 { + self.source.push_str(", "); + } + self.print_ty(ty, true); + } + self.source.push_str("]"); + } + } params } @@ -335,11 +386,11 @@ impl<'s, 'd, 'i> SourceBuilder<'s, 'd, 'i> { /// @dataclass /// class Foo0: /// value: int - /// + /// /// @dataclass /// class Foo1: /// value: int - /// + /// /// Foo = Union[Foo0, Foo1] /// ``` pub fn print_union_wrapped(&mut self, name: &str, union: &Union, docs: &Docs) { diff --git a/crates/gen-host-wasmtime-rust/src/lib.rs b/crates/gen-host-wasmtime-rust/src/lib.rs index 163b0cb11..bd7f2ee7d 100644 --- a/crates/gen-host-wasmtime-rust/src/lib.rs +++ b/crates/gen-host-wasmtime-rust/src/lib.rs @@ -88,7 +88,7 @@ enum FunctionRet { /// The function returns a `Result` in both wasm and in Rust, but the /// Rust error type is a custom error and must be converted to `err`. The /// `ok` variant payload is provided here too. - CustomToError { ok: Type, err: String }, + CustomToError { ok: Option, err: String }, } impl Wasmtime { @@ -143,15 +143,17 @@ impl Wasmtime { return FunctionRet::Normal; } - if let Type::Id(id) = &f.result { - if let TypeDefKind::Result(r) = &iface.types[*id].kind { - if let Type::Id(err) = r.err { - if let Some(name) = &iface.types[err].name { - self.needs_custom_error_to_types.insert(name.clone()); - return FunctionRet::CustomToError { - ok: r.ok, - err: name.to_string(), - }; + if f.results.len() == 1 { + if let Type::Id(id) = f.results.iter_types().next().unwrap() { + if let TypeDefKind::Result(r) = &iface.types[*id].kind { + if let Some(Type::Id(err)) = r.err { + if let Some(name) = &iface.types[err].name { + self.needs_custom_error_to_types.insert(name.clone()); + return FunctionRet::CustomToError { + ok: r.ok, + err: name.to_string(), + }; + } } } } @@ -160,6 +162,26 @@ impl Wasmtime { self.needs_custom_error_to_trap = true; FunctionRet::CustomToTrap } + + fn print_result_ty(&mut self, iface: &Interface, results: &Results, mode: TypeMode) { + match results { + Results::Named(rs) => match rs.len() { + 0 => self.push_str("()"), + 1 => self.print_ty(iface, &rs[0].1, mode), + _ => { + self.push_str("("); + for (i, (_, ty)) in rs.iter().enumerate() { + if i > 0 { + self.push_str(", ") + } + self.print_ty(iface, ty, mode) + } + self.push_str(")"); + } + }, + Results::Anon(ty) => self.print_ty(iface, ty, mode), + } + } } impl RustGenerator for Wasmtime { @@ -509,16 +531,20 @@ impl Generator for Wasmtime { match self.classify_fn_ret(iface, func) { FunctionRet::Normal => { self.push_str(" -> "); - self.print_ty(iface, &func.result, TypeMode::Owned); + self.print_result_ty(iface, &func.results, TypeMode::Owned); } FunctionRet::CustomToTrap => { self.push_str(" -> Result<"); - self.print_ty(iface, &func.result, TypeMode::Owned); + self.print_result_ty(iface, &func.results, TypeMode::Owned); self.push_str(", Self::Error>"); } FunctionRet::CustomToError { ok, .. } => { self.push_str(" -> Result<"); - self.print_ty(iface, &ok, TypeMode::Owned); + if let Some(ok) = ok { + self.print_ty(iface, &ok, TypeMode::Owned); + } else { + self.push_str("()"); + } self.push_str(", Self::Error>"); } } @@ -611,7 +637,22 @@ impl Generator for Wasmtime { sig.self_arg = Some("&self, mut caller: impl wasmtime::AsContextMut".to_string()); self.print_docs_and_params(iface, func, TypeMode::AllBorrowed("'_"), &sig); self.push_str("-> Result<"); - self.print_ty(iface, &func.result, TypeMode::Owned); + match func.results.len() { + 0 => self.push_str("()"), + 1 => self.print_ty( + iface, + func.results.iter_types().next().unwrap(), + TypeMode::Owned, + ), + _ => { + self.push_str("("); + for ty in func.results.iter_types() { + self.print_ty(iface, ty, TypeMode::Owned); + self.push_str(", "); + } + self.push_str(")"); + } + } self.push_str(", wasmtime::Trap> {\n"); let params = func @@ -1378,13 +1419,6 @@ impl Bindgen for FunctionBindgen<'_> { wit_bindgen_gen_rust_lib::bitcast(casts, operands, results) } - Instruction::UnitLower => { - self.push_str(&format!("let () = {};\n", operands[0])); - } - Instruction::UnitLift => { - results.push("()".to_string()); - } - Instruction::I32FromBool => { results.push(format!("match {} {{ true => 1, false => 0 }}", operands[0])); } @@ -1505,8 +1539,8 @@ impl Bindgen for FunctionBindgen<'_> { for (case, block) in variant.cases.iter().zip(blocks) { let case_name = case.name.to_camel_case(); self.push_str(&format!("{name}::{case_name}")); - if case.ty == Type::Unit { - self.push_str(&format!(" => {{\nlet e = ();\n{block}\n}}\n")); + if case.ty.is_none() { + self.push_str(&format!(" => {{\n{block}\n}}\n")); } else { self.push_str(&format!("(e) => {block},\n")); } @@ -1523,7 +1557,7 @@ impl Bindgen for FunctionBindgen<'_> { let mut result = format!("match {op0} {{\n"); let name = self.typename_lift(iface, *ty); for (i, (case, block)) in variant.cases.iter().zip(blocks).enumerate() { - let block = if case.ty != Type::Unit { + let block = if case.ty.is_some() { format!("({block})") } else { String::new() @@ -1595,7 +1629,7 @@ impl Bindgen for FunctionBindgen<'_> { self.push_str(&format!( "match {operand} {{ Some(e) => {some}, - None => {{\nlet e = ();\n{none}\n}}, + None => {{\n{none}\n}}, }};" )); } @@ -1617,16 +1651,19 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::ResultLower { results: result_types, + result, .. } => { let err = self.blocks.pop().unwrap(); let ok = self.blocks.pop().unwrap(); self.let_results(result_types.len(), results); let operand = &operands[0]; + let ok_binding = if result.ok.is_some() { "e" } else { "()" }; + let err_binding = if result.err.is_some() { "e" } else { "()" }; self.push_str(&format!( "match {operand} {{ - Ok(e) => {{ {ok} }}, - Err(e) => {{ {err} }}, + Ok({ok_binding}) => {{ {ok} }}, + Err({err_binding}) => {{ {err} }}, }};" )); } @@ -1936,8 +1973,7 @@ impl Bindgen for FunctionBindgen<'_> { } call.push_str(")"); - self.push_str("let result = "); - results.push("result".to_string()); + self.let_results(func.results.len(), results); match self.gen.classify_fn_ret(iface, func) { FunctionRet::Normal => self.push_str(&call), // Unwrap the result, translating errors to unconditional @@ -1967,18 +2003,15 @@ impl Bindgen for FunctionBindgen<'_> { } self.push_str(";\n"); self.after_call = true; - match &func.result { - Type::Unit => {} - _ if self.gen.opts.tracing => { - self.push_str("wit_bindgen_host_wasmtime_rust::tracing::event!(\n"); - self.push_str("wit_bindgen_host_wasmtime_rust::tracing::Level::TRACE,\n"); + if self.gen.opts.tracing && func.results.len() > 0 { + self.push_str("wit_bindgen_host_wasmtime_rust::tracing::event!(\n"); + self.push_str("wit_bindgen_host_wasmtime_rust::tracing::Level::TRACE,\n"); + for result in results.iter() { self.push_str(&format!( - "{} = wit_bindgen_host_wasmtime_rust::tracing::field::debug(&{0}),\n", - results[0], + "{result} = wit_bindgen_host_wasmtime_rust::tracing::field::debug(&{result}),\n", )); - self.push_str(");\n"); } - _ => {} + self.push_str(");\n"); } } diff --git a/crates/gen-host-wasmtime-rust/tests/codegen.rs b/crates/gen-host-wasmtime-rust/tests/codegen.rs index 27e4b5192..95daa8db9 100644 --- a/crates/gen-host-wasmtime-rust/tests/codegen.rs +++ b/crates/gen-host-wasmtime-rust/tests/codegen.rs @@ -42,7 +42,7 @@ mod custom_errors { wit_bindgen_host_wasmtime_rust::export!({ src["x"]: " foo: func() - bar: func() -> result + bar: func() -> result<_, u32> enum errno { bad1, bad2, diff --git a/crates/gen-markdown/src/lib.rs b/crates/gen-markdown/src/lib.rs index 929140691..b03a30f8b 100644 --- a/crates/gen-markdown/src/lib.rs +++ b/crates/gen-markdown/src/lib.rs @@ -35,7 +35,6 @@ impl Markdown { fn print_ty(&mut self, iface: &Interface, ty: &Type, skip_name: bool) { match ty { - Type::Unit => self.src.push_str("`unit`"), Type::Bool => self.src.push_str("`bool`"), Type::U8 => self.src.push_str("`u8`"), Type::S8 => self.src.push_str("`s8`"), @@ -90,30 +89,65 @@ impl Markdown { self.print_ty(iface, t, false); self.src.push_str(">"); } - TypeDefKind::Result(r) => { - self.src.push_str("result<"); - self.print_ty(iface, &r.ok, false); - self.src.push_str(", "); - self.print_ty(iface, &r.err, false); - self.src.push_str(">"); - } + TypeDefKind::Result(r) => match (r.ok, r.err) { + (Some(ok), Some(err)) => { + self.src.push_str("result<"); + self.print_ty(iface, &ok, false); + self.src.push_str(", "); + self.print_ty(iface, &err, false); + self.src.push_str(">"); + } + (None, Some(err)) => { + self.src.push_str("result<_, "); + self.print_ty(iface, &err, false); + self.src.push_str(">"); + } + (Some(ok), None) => { + self.src.push_str("result<"); + self.print_ty(iface, &ok, false); + self.src.push_str(">"); + } + (None, None) => { + self.src.push_str("result"); + } + }, TypeDefKind::List(t) => { self.src.push_str("list<"); self.print_ty(iface, t, false); self.src.push_str(">"); } - TypeDefKind::Future(t) => { - self.src.push_str("future<"); - self.print_ty(iface, t, false); - self.src.push_str(">"); - } - TypeDefKind::Stream(s) => { - self.src.push_str("stream<"); - self.print_ty(iface, &s.element, false); - self.src.push_str(", "); - self.print_ty(iface, &s.end, false); - self.src.push_str(">"); - } + TypeDefKind::Future(t) => match t { + Some(t) => { + self.src.push_str("future<"); + self.print_ty(iface, t, false); + self.src.push_str(">"); + } + None => { + self.src.push_str("future"); + } + }, + TypeDefKind::Stream(s) => match (s.element, s.end) { + (Some(element), Some(end)) => { + self.src.push_str("stream<"); + self.print_ty(iface, &element, false); + self.src.push_str(", "); + self.print_ty(iface, &end, false); + self.src.push_str(">"); + } + (None, Some(end)) => { + self.src.push_str("stream<_, "); + self.print_ty(iface, &end, false); + self.src.push_str(">"); + } + (Some(element), None) => { + self.src.push_str("stream<"); + self.print_ty(iface, &element, false); + self.src.push_str(">"); + } + (None, None) => { + self.src.push_str("stream"); + } + }, } } } @@ -274,8 +308,10 @@ impl Generator for Markdown { format!("{}::{}", name, case.name), format!("#{}.{}", name.to_snake_case(), case.name.to_snake_case()), ); - self.src.push_str(": "); - self.print_ty(iface, &case.ty, false); + if let Some(ty) = &case.ty { + self.src.push_str(": "); + self.print_ty(iface, ty, false); + } self.src.indent(1); self.src.push_str("\n\n"); self.docs(&case.docs); @@ -361,11 +397,28 @@ impl Generator for Markdown { docs: &Docs, ) { self.print_type_header(name); - self.src.push_str("result<"); - self.print_ty(iface, &result.ok, false); - self.src.push_str(", "); - self.print_ty(iface, &result.err, false); - self.src.push_str(">"); + match (result.ok, result.err) { + (Some(ok), Some(err)) => { + self.src.push_str("result<"); + self.print_ty(iface, &ok, false); + self.src.push_str(", "); + self.print_ty(iface, &err, false); + self.src.push_str(">"); + } + (None, Some(err)) => { + self.src.push_str("result<_, "); + self.print_ty(iface, &err, false); + self.src.push_str(">"); + } + (Some(ok), None) => { + self.src.push_str("result<"); + self.print_ty(iface, &ok, false); + self.src.push_str(">"); + } + (None, None) => { + self.src.push_str("result"); + } + } self.print_type_info(id, docs); } @@ -420,12 +473,12 @@ impl Generator for Markdown { self.src.push_str("\n"); } } - match &func.result { - Type::Unit => {} - ty => { - self.src.push_str("##### Results\n\n"); + + if func.results.len() > 0 { + self.src.push_str("##### Results\n\n"); + for (i, ty) in func.results.iter_types().enumerate() { self.src.push_str(&format!( - "- `{}`: ", + "- `{}{i}`: ", "result", f = func.name.to_snake_case(), p = "result", diff --git a/crates/gen-rust-lib/src/lib.rs b/crates/gen-rust-lib/src/lib.rs index e27228f97..ff568b383 100644 --- a/crates/gen-rust-lib/src/lib.rs +++ b/crates/gen-rust-lib/src/lib.rs @@ -89,7 +89,7 @@ pub trait RustGenerator { ) -> Vec { let params = self.print_docs_and_params(iface, func, param_mode, &sig); self.push_str(" -> "); - self.print_ty(iface, &func.result, TypeMode::Owned); + self.print_result_params(iface, &func.results, TypeMode::Owned); params } @@ -146,6 +146,21 @@ pub trait RustGenerator { params } + fn print_result_params(&mut self, iface: &Interface, results: &Results, mode: TypeMode) { + match results.len() { + 0 => self.push_str("()"), + 1 => self.print_ty(iface, results.iter_types().next().unwrap(), mode), + _ => { + self.push_str("("); + for ty in results.iter_types() { + self.print_ty(iface, ty, mode); + self.push_str(", ") + } + self.push_str(")") + } + } + } + fn print_ty(&mut self, iface: &Interface, ty: &Type, mode: TypeMode) { match ty { Type::Id(t) => self.print_tyid(iface, *t, mode), @@ -182,7 +197,6 @@ pub trait RustGenerator { self.push_str(suffix); } - Type::Unit => self.push_str("()"), Type::Bool => self.push_str("bool"), Type::U8 => self.push_str("u8"), Type::U16 => self.push_str("u16"), @@ -204,6 +218,13 @@ pub trait RustGenerator { } } + fn print_optional_ty(&mut self, iface: &Interface, ty: Option<&Type>, mode: TypeMode) { + match ty { + Some(ty) => self.print_ty(iface, ty, mode), + None => self.push_str("()"), + } + } + fn print_tyid(&mut self, iface: &Interface, id: TypeId, mode: TypeMode) { let info = self.info(id); let lt = self.lifetime_for(&info, mode); @@ -257,9 +278,9 @@ pub trait RustGenerator { TypeDefKind::Result(r) => { self.push_str("Result<"); - self.print_ty(iface, &r.ok, mode); + self.print_optional_ty(iface, r.ok.as_ref(), mode); self.push_str(","); - self.print_ty(iface, &r.err, mode); + self.print_optional_ty(iface, r.err.as_ref(), mode); self.push_str(">"); } @@ -290,14 +311,14 @@ pub trait RustGenerator { } TypeDefKind::Future(ty) => { self.push_str("Future<"); - self.print_ty(iface, ty, mode); + self.print_optional_ty(iface, ty.as_ref(), mode); self.push_str(">"); } TypeDefKind::Stream(stream) => { self.push_str("Stream<"); - self.print_ty(iface, &stream.element, mode); + self.print_optional_ty(iface, stream.element.as_ref(), mode); self.push_str(","); - self.print_ty(iface, &stream.end, mode); + self.print_optional_ty(iface, stream.end.as_ref(), mode); self.push_str(">"); } @@ -394,7 +415,6 @@ pub trait RustGenerator { /// Writes the camel-cased 'name' of the passed type to `out`, as used to name union variants. fn write_name(&self, iface: &Interface, ty: &Type, out: &mut String) { match ty { - Type::Unit => out.push_str("Unit"), Type::Bool => out.push_str("Bool"), Type::U8 => out.push_str("U8"), Type::U16 => out.push_str("U16"), @@ -425,12 +445,12 @@ pub trait RustGenerator { out.push_str("List") } TypeDefKind::Future(ty) => { - self.write_name(iface, ty, out); + self.write_optional_name(iface, ty.as_ref(), out); out.push_str("Future"); } TypeDefKind::Stream(s) => { - self.write_name(iface, &s.element, out); - self.write_name(iface, &s.end, out); + self.write_optional_name(iface, s.element.as_ref(), out); + self.write_optional_name(iface, s.end.as_ref(), out); out.push_str("Stream"); } @@ -446,6 +466,13 @@ pub trait RustGenerator { } } + fn write_optional_name(&self, iface: &Interface, ty: Option<&Type>, out: &mut String) { + match ty { + Some(ty) => self.write_name(iface, ty, out), + None => out.push_str("()"), + } + } + /// Returns the names for the cases of the passed union. fn union_case_names(&self, iface: &Interface, union: &Union) -> Vec { enum UsedState<'a> { @@ -580,7 +607,7 @@ pub trait RustGenerator { variant .cases .iter() - .map(|c| (c.name.to_camel_case(), &c.docs, &c.ty)), + .map(|c| (c.name.to_camel_case(), &c.docs, c.ty.as_ref())), docs, ); } @@ -593,7 +620,7 @@ pub trait RustGenerator { iface, id, zip(self.union_case_names(iface, union), &union.cases) - .map(|(name, case)| (name, &case.docs, &case.ty)), + .map(|(name, case)| (name, &case.docs, Some(&case.ty))), docs, ); } @@ -602,7 +629,7 @@ pub trait RustGenerator { &mut self, iface: &Interface, id: TypeId, - cases: impl IntoIterator + Clone, + cases: impl IntoIterator)> + Clone, docs: &Docs, ) where Self: Sized, @@ -624,9 +651,9 @@ pub trait RustGenerator { for (case_name, docs, payload) in cases.clone() { self.rustdoc(docs); self.push_str(&case_name); - if *payload != Type::Unit { + if let Some(ty) = payload { self.push_str("("); - self.print_ty(iface, payload, mode); + self.print_ty(iface, ty, mode); self.push_str(")") } self.push_str(",\n"); @@ -650,7 +677,7 @@ pub trait RustGenerator { id: TypeId, mode: TypeMode, name: &str, - cases: impl IntoIterator, + cases: impl IntoIterator)>, ) where Self: Sized, { @@ -668,12 +695,12 @@ pub trait RustGenerator { self.push_str(name); self.push_str("::"); self.push_str(&case_name); - if *payload != Type::Unit { + if payload.is_some() { self.push_str("(e)"); } self.push_str(" => {\n"); self.push_str(&format!("f.debug_tuple(\"{}::{}\")", name, case_name)); - if *payload != Type::Unit { + if payload.is_some() { self.push_str(".field(e)"); } self.push_str(".finish()\n"); @@ -713,9 +740,9 @@ pub trait RustGenerator { self.push_str(&format!("pub type {}", name)); self.print_generics(&info, lt, true); self.push_str("= Result<"); - self.print_ty(iface, &result.ok, mode); + self.print_optional_ty(iface, result.ok.as_ref(), mode); self.push_str(","); - self.print_ty(iface, &result.err, mode); + self.print_optional_ty(iface, result.err.as_ref(), mode); self.push_str(">;\n"); } } @@ -809,10 +836,7 @@ pub trait RustGenerator { id, TypeMode::Owned, &name, - enum_ - .cases - .iter() - .map(|c| (c.name.to_camel_case(), &Type::Unit)), + enum_.cases.iter().map(|c| (c.name.to_camel_case(), None)), ) } } diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index d7694b2c8..520c2b814 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -84,12 +84,21 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { .iter() .map(|(_, t)| quote_ty(true, iface, t)) .collect::>(); - let ret = quote_ty(false, iface, &f.result); + let rets = f + .results + .iter_types() + .map(|t| quote_ty(false, iface, t)) + .collect::>(); let mut self_ = quote::quote!(); if let FunctionKind::Method { .. } = &f.kind { params.remove(0); self_ = quote::quote!(&self,); } + let ret = match rets.len() { + 0 => quote::quote!(()), + 1 => rets[0].clone(), + _ => quote::quote!((#(#rets,)*)), + }; let method = quote::quote! { fn #name(#self_ #(_: #params),*) -> #ret { loop {} @@ -130,7 +139,6 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { ty: &wit_parser::Type, ) -> proc_macro2::TokenStream { match *ty { - Type::Unit => quote::quote! { () }, Type::Bool => quote::quote! { bool }, Type::U8 => quote::quote! { u8 }, Type::S8 => quote::quote! { i8 }, @@ -184,8 +192,14 @@ pub fn codegen_rust_wasm_export(input: TokenStream) -> TokenStream { quote::quote! { Option<#ty> } } TypeDefKind::Result(r) => { - let ok = quote_ty(param, iface, &r.ok); - let err = quote_ty(param, iface, &r.err); + let ok = match &r.ok { + Some(t) => quote_ty(param, iface, t), + None => quote::quote!(()), + }; + let err = match &r.err { + Some(t) => quote_ty(param, iface, t), + None => quote::quote!(()), + }; quote::quote! { Result<#ok, #err> } } TypeDefKind::Future(_) => todo!("unknown future"), diff --git a/crates/wit-component/Cargo.toml b/crates/wit-component/Cargo.toml index f4491954e..03e795bb1 100644 --- a/crates/wit-component/Cargo.toml +++ b/crates/wit-component/Cargo.toml @@ -20,8 +20,8 @@ path = "src/bin/wasm2wit.rs" required-features = ["cli"] [dependencies] -wasmparser = "0.86.0" -wasm-encoder = "0.13.0" +wasmparser = "0.90.0" +wasm-encoder = "0.16.0" wat = "1.0.48" wit-parser = { path = "../wit-parser" } anyhow = "1.0.55" @@ -31,7 +31,7 @@ env_logger = { version = "0.9.0", optional = true } log = { version = "0.4.14", optional = true } [dev-dependencies] -wasmprinter = "0.2.36" +wasmprinter = "0.2.39" glob = "0.3.0" pretty_assertions = "1.2.0" diff --git a/crates/wit-component/src/decoding.rs b/crates/wit-component/src/decoding.rs index 025ed6ee2..48dac72c9 100644 --- a/crates/wit-component/src/decoding.rs +++ b/crates/wit-component/src/decoding.rs @@ -225,12 +225,13 @@ impl<'a> InterfaceDecoder<'a> { Ok(self.interface) } - fn add_function(&mut self, func_name: &str, ty: &types::ComponentFuncType) -> Result<()> { - validate_id(func_name) - .with_context(|| format!("function name `{}` is not a valid identifier", func_name))?; - + fn decode_params( + &mut self, + func_name: &str, + ps: &[(Option, types::ComponentValType)], + ) -> Result { let mut params = Vec::new(); - for (name, ty) in ty.params.iter() { + for (name, ty) in ps.iter() { let name = name .as_ref() .ok_or_else(|| anyhow!("function `{}` has a parameter without a name", func_name))? @@ -245,15 +246,78 @@ impl<'a> InterfaceDecoder<'a> { params.push((name, self.decode_type(ty)?)); } + Ok(params) + } + + fn decode_results( + &mut self, + func_name: &str, + ps: &[(Option, types::ComponentValType)], + ) -> Result { + let mut results = Vec::new(); + for (name, ty) in ps.iter() { + let name = match name { + Some(name) => { + let name = name.to_string(); + validate_id(&name).with_context(|| { + format!( + "function `{}` has a result type `{}` that is not a valid identifier", + func_name, name + ) + })?; + Some(name) + } + None => None, + }; + + results.push((name, self.decode_type(ty)?)); + } + + // Results must be either + // - A single anonymous type + // - Any number of named types + match results.len() { + 1 => { + // We either have a single anonymous type or a single + // named type. Either is valid. + let (name, ty) = results.into_iter().next().unwrap(); + match name { + Some(name) => Ok(Results::Named(vec![(name, ty)])), + None => Ok(Results::Anon(ty)), + } + } + _ => { + // Otherwise, all types must be named. + let mut rs = Vec::new(); + for (name, ty) in results.into_iter() { + match name { + Some(name) => rs.push((name, ty)), + None => { + return Err(anyhow!( + "function `{}` is missing a result type name", + func_name + )) + } + } + } + Ok(Results::Named(rs)) + } + } + } - let result = self.decode_type(&ty.result)?; + fn add_function(&mut self, func_name: &str, ty: &types::ComponentFuncType) -> Result<()> { + validate_id(func_name) + .with_context(|| format!("function name `{}` is not a valid identifier", func_name))?; + + let params = self.decode_params(func_name, &ty.params)?; + let results = self.decode_results(func_name, &ty.results)?; self.interface.functions.push(Function { docs: Docs::default(), name: func_name.to_string(), kind: FunctionKind::Freestanding, params, - result, + results, }); Ok(()) @@ -303,8 +367,8 @@ impl<'a> InterfaceDecoder<'a> { self.decode_union(name, &u.types)? } types::ComponentDefinedType::Option(ty) => self.decode_option(name, ty)?, - types::ComponentDefinedType::Expected(ok, err) => { - self.decode_result(name, ok, err)? + types::ComponentDefinedType::Result { ok, err } => { + self.decode_result(name, ok.as_ref(), err.as_ref())? } }, _ => unreachable!(), @@ -316,6 +380,16 @@ impl<'a> InterfaceDecoder<'a> { }) } + fn decode_optional_type( + &mut self, + ty: Option<&types::ComponentValType>, + ) -> Result> { + match ty { + Some(ty) => self.decode_type(ty).map(Some), + None => Ok(None), + } + } + fn decode_named_primitive( &mut self, name: Option, @@ -334,7 +408,6 @@ impl<'a> InterfaceDecoder<'a> { fn decode_primitive(&mut self, ty: PrimitiveValType) -> Result { Ok(match ty { - PrimitiveValType::Unit => Type::Unit, PrimitiveValType::Bool => Type::Bool, PrimitiveValType::S8 => Type::S8, PrimitiveValType::U8 => Type::U8, @@ -405,7 +478,7 @@ impl<'a> InterfaceDecoder<'a> { Ok(Case { docs: Docs::default(), name: name.to_string(), - ty: self.decode_type(&case.ty)?, + ty: self.decode_optional_type(case.ty.as_ref())?, }) }) .collect::>()?, @@ -526,11 +599,11 @@ impl<'a> InterfaceDecoder<'a> { fn decode_result( &mut self, name: Option, - ok: &types::ComponentValType, - err: &types::ComponentValType, + ok: Option<&types::ComponentValType>, + err: Option<&types::ComponentValType>, ) -> Result { - let ok = self.decode_type(ok)?; - let err = self.decode_type(err)?; + let ok = self.decode_optional_type(ok)?; + let err = self.decode_optional_type(err)?; Ok(Type::Id(self.alloc_type( name, TypeDefKind::Result(Result_ { ok, err }), diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 92a2235a6..bf78cf75e 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -12,8 +12,8 @@ use wasm_encoder::*; use wasmparser::{Validator, WasmFeatures}; use wit_parser::{ abi::{AbiVariant, WasmSignature, WasmType}, - Enum, Flags, Function, FunctionKind, Interface, Record, Result_, Tuple, Type, TypeDef, - TypeDefKind, Union, Variant, + Enum, Flags, Function, FunctionKind, Interface, Params, Record, Result_, Results, Tuple, Type, + TypeDef, TypeDefKind, Union, Variant, }; const INDIRECT_TABLE_NAME: &str = "$imports"; @@ -123,12 +123,18 @@ impl PartialEq for TypeDefKey<'_> { v1.cases.iter().zip(v2.cases.iter()).all(|(c1, c2)| { c1.name == c2.name - && TypeKey { - interface: self.interface, - ty: c1.ty, - } == TypeKey { - interface: other.interface, - ty: c2.ty, + && match (c1.ty, c2.ty) { + (Some(ty1), Some(ty2)) => { + TypeKey { + interface: self.interface, + ty: ty1, + } == TypeKey { + interface: other.interface, + ty: ty2, + } + } + (None, None) => true, + _ => false, } }) } @@ -168,19 +174,33 @@ impl PartialEq for TypeDefKey<'_> { ty: *t2, }), (TypeDefKind::Result(r1), TypeDefKind::Result(r2)) => { - TypeKey { - interface: self.interface, - ty: r1.ok, - } == TypeKey { - interface: other.interface, - ty: r2.ok, - } && TypeKey { - interface: self.interface, - ty: r1.err, - } == TypeKey { - interface: other.interface, - ty: r2.err, - } + let ok_eq = match (r1.ok, r2.ok) { + (Some(ok1), Some(ok2)) => { + TypeKey { + interface: self.interface, + ty: ok1, + } == TypeKey { + interface: other.interface, + ty: ok2, + } + } + (None, None) => true, + _ => false, + }; + let err_eq = match (r1.err, r2.err) { + (Some(err1), Some(err2)) => { + TypeKey { + interface: self.interface, + ty: err1, + } == TypeKey { + interface: other.interface, + ty: err2, + } + } + (None, None) => true, + _ => false, + }; + ok_eq && err_eq } _ => false, } @@ -225,11 +245,13 @@ impl Hash for TypeDefKey<'_> { state.write_u8(3); for c in &v.cases { c.name.hash(state); - TypeKey { - interface: self.interface, - ty: c.ty, + if let Some(ty) = c.ty { + TypeKey { + interface: self.interface, + ty, + } + .hash(state); } - .hash(state); } } TypeDefKind::Enum(e) => { @@ -264,16 +286,20 @@ impl Hash for TypeDefKey<'_> { } TypeDefKind::Result(r) => { state.write_u8(8); - TypeKey { - interface: self.interface, - ty: r.ok, + if let Some(ok) = r.ok { + TypeKey { + interface: self.interface, + ty: ok, + } + .hash(state); } - .hash(state); - TypeKey { - interface: self.interface, - ty: r.err, + if let Some(err) = r.err { + TypeKey { + interface: self.interface, + ty: err, + } + .hash(state); } - .hash(state); } TypeDefKind::Union(u) => { state.write_u8(9); @@ -304,29 +330,31 @@ impl PartialEq for FunctionKey<'_> { return false; } - self.func - .params - .iter() - .zip(other.func.params.iter()) - .all(|((n1, t1), (n2, t2))| { - n1 == n2 - && TypeKey { - interface: self.interface, - ty: *t1, - } - .eq(&TypeKey { - interface: other.interface, - ty: *t2, - }) - }) - && TypeKey { + let key_equal = |t1, t2| { + TypeKey { interface: self.interface, - ty: self.func.result, + ty: t1, } .eq(&TypeKey { interface: other.interface, - ty: other.func.result, + ty: t2, }) + }; + + let params_equal = |ps: &Params, ops: &Params| { + ps.iter() + .zip(ops.iter()) + .all(|((n1, t1), (n2, t2))| n1 == n2 && key_equal(*t1, *t2)) + }; + + let results_equal = |rs: &Results, ors: &Results| match (rs, ors) { + (Results::Named(rs), Results::Named(ors)) => params_equal(rs, ors), + (Results::Anon(ty), Results::Anon(oty)) => key_equal(*ty, *oty), + _ => false, + }; + + params_equal(&self.func.params, &other.func.params) + && results_equal(&self.func.results, &other.func.results) } } @@ -335,7 +363,7 @@ impl Eq for FunctionKey<'_> {} impl Hash for FunctionKey<'_> { fn hash(&self, state: &mut H) { self.func.params.len().hash(state); - for (name, ty) in &self.func.params { + for (name, ty) in self.func.params.iter() { name.hash(state); TypeKey { interface: self.interface, @@ -343,11 +371,25 @@ impl Hash for FunctionKey<'_> { } .hash(state); } - TypeKey { - interface: self.interface, - ty: self.func.result, + match &self.func.results { + Results::Named(rs) => { + for (name, ty) in rs.iter() { + name.hash(state); + TypeKey { + interface: self.interface, + ty: *ty, + } + .hash(state); + } + } + Results::Anon(ty) => { + TypeKey { + interface: self.interface, + ty: *ty, + } + .hash(state); + } } - .hash(state); } } @@ -485,6 +527,23 @@ impl<'a> TypeEncoder<'a> { index } + fn encode_params( + &mut self, + interface: &'a Interface, + params: &'a Params, + export_named_types: bool, + ) -> Result> { + params + .iter() + .map(|(name, ty)| { + Ok(( + name.as_str(), + self.encode_valtype(interface, ty, export_named_types)?, + )) + }) + .collect::>() + } + fn encode_func_type( &mut self, interface: &'a Interface, @@ -497,21 +556,30 @@ impl<'a> TypeEncoder<'a> { } // Encode all referenced parameter types from this function. - let params: Vec<_> = func - .params - .iter() - .map(|(name, ty)| { - Ok(( - Some(name.as_str()), - self.encode_valtype(interface, ty, export_named_types)?, - )) - }) - .collect::>()?; - let result = self.encode_valtype(interface, &func.result, export_named_types)?; + let params: Vec<_> = self.encode_params(interface, &func.params, export_named_types)?; + + enum EncodedResults<'a> { + Named(Vec<(&'a str, ComponentValType)>), + Anon(ComponentValType), + } + + let results = match &func.results { + Results::Named(rs) => { + EncodedResults::Named(self.encode_params(interface, rs, export_named_types)?) + } + Results::Anon(ty) => { + EncodedResults::Anon(self.encode_valtype(interface, ty, export_named_types)?) + } + }; // Encode the function type let index = self.types.len(); - self.types.function(params, result); + let mut f = self.types.function(); + f.params(params); + match results { + EncodedResults::Named(rs) => f.results(rs), + EncodedResults::Anon(ty) => f.result(ty), + }; self.func_type_map.insert(key, index); Ok(index) } @@ -523,7 +591,6 @@ impl<'a> TypeEncoder<'a> { export_named_types: bool, ) -> Result { Ok(match ty { - Type::Unit => ComponentValType::Primitive(PrimitiveValType::Unit), Type::Bool => ComponentValType::Primitive(PrimitiveValType::Bool), Type::U8 => ComponentValType::Primitive(PrimitiveValType::U8), Type::U16 => ComponentValType::Primitive(PrimitiveValType::U16), @@ -612,6 +679,20 @@ impl<'a> TypeEncoder<'a> { }) } + fn encode_optional_valtype( + &mut self, + interface: &'a Interface, + ty: Option<&Type>, + export_named_types: bool, + ) -> Result> { + match ty { + Some(ty) => self + .encode_valtype(interface, ty, export_named_types) + .map(Some), + None => Ok(None), + } + } + fn encode_record( &mut self, interface: &'a Interface, @@ -671,7 +752,7 @@ impl<'a> TypeEncoder<'a> { .map(|c| { Ok(( c.name.as_str(), - self.encode_valtype(interface, &c.ty, export_named_types)?, + self.encode_optional_valtype(interface, c.ty.as_ref(), export_named_types)?, None, // TODO: support defaulting case values in the future )) }) @@ -720,11 +801,12 @@ impl<'a> TypeEncoder<'a> { result: &Result_, export_named_types: bool, ) -> Result { - let ok = self.encode_valtype(interface, &result.ok, export_named_types)?; - let error = self.encode_valtype(interface, &result.err, export_named_types)?; + let ok = self.encode_optional_valtype(interface, result.ok.as_ref(), export_named_types)?; + let error = + self.encode_optional_valtype(interface, result.err.as_ref(), export_named_types)?; let index = self.types.len(); let encoder = self.types.defined_type(); - encoder.expected(ok, error); + encoder.result(ok, error); Ok(ComponentValType::Type(index)) } @@ -814,6 +896,20 @@ impl RequiredOptions { } } + fn for_optional_types<'a>( + interface: &Interface, + types: impl Iterator>, + ) -> Self { + Self::for_types(interface, types.flatten()) + } + + fn for_optional_type(interface: &Interface, ty: Option<&Type>) -> Self { + match ty { + Some(ty) => Self::for_type(interface, ty), + None => Self::None, + } + } + fn for_type(interface: &Interface, ty: &Type) -> Self { match ty { Type::Id(id) => match &interface.types[*id].kind { @@ -824,10 +920,11 @@ impl RequiredOptions { TypeDefKind::Flags(_) => Self::None, TypeDefKind::Option(t) => Self::for_type(interface, t), TypeDefKind::Result(r) => { - Self::for_type(interface, &r.ok) | Self::for_type(interface, &r.err) + Self::for_optional_type(interface, r.ok.as_ref()) + | Self::for_optional_type(interface, r.err.as_ref()) } TypeDefKind::Variant(v) => { - Self::for_types(interface, v.cases.iter().map(|c| &c.ty)) + Self::for_optional_types(interface, v.cases.iter().map(|c| c.ty.as_ref())) } TypeDefKind::Union(v) => Self::for_types(interface, v.cases.iter().map(|c| &c.ty)), TypeDefKind::Enum(_) => Self::None, @@ -853,7 +950,7 @@ impl RequiredOptions { .params .iter() .map(|(_, ty)| ty) - .chain([&function.result]), + .chain(function.results.iter_types()), ) } @@ -1217,7 +1314,7 @@ impl EncodingState { elements.active( None, - &Instruction::I32Const(0), + &ConstExpr::i32_const(0), ValType::FuncRef, Elements::Functions(&func_indexes), ); diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index 88c74387b..ec82068e1 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -2,7 +2,8 @@ use anyhow::{bail, Result}; use indexmap::IndexSet; use std::fmt::Write; use wit_parser::{ - Enum, Flags, Interface, Record, Result_, Tuple, Type, TypeDefKind, TypeId, Union, Variant, + Enum, Flags, Interface, Record, Result_, Results, Tuple, Type, TypeDefKind, TypeId, Union, + Variant, }; /// A utility for printing WebAssembly interface definitions to a string. @@ -16,7 +17,12 @@ impl InterfacePrinter { /// Print the given WebAssembly interface to a string. pub fn print(&mut self, interface: &Interface) -> Result { for func in &interface.functions { - for ty in func.params.iter().map(|p| &p.1).chain([&func.result]) { + for ty in func + .params + .iter() + .map(|(_, ty)| ty) + .chain(func.results.iter_types()) + { self.declare_type(interface, ty)?; } } @@ -32,13 +38,21 @@ impl InterfacePrinter { } self.output.push(')'); - match &func.result { - Type::Unit => {} - other => { + match &func.results { + Results::Named(rs) => match rs.len() { + 0 => (), + 1 => { + self.output.push_str(" -> "); + self.print_type_name(interface, &rs[0].1)?; + } + _ => todo!("multireturn: wit component printing"), + }, + Results::Anon(ty) => { self.output.push_str(" -> "); - self.print_type_name(interface, other)?; + self.print_type_name(interface, ty)?; } } + self.output.push_str("\n\n"); } @@ -48,7 +62,6 @@ impl InterfacePrinter { fn print_type_name(&mut self, interface: &Interface, ty: &Type) -> Result<()> { match ty { - Type::Unit => self.output.push_str("unit"), Type::Bool => self.output.push_str("bool"), Type::U8 => self.output.push_str("u8"), Type::U16 => self.output.push_str("u16"), @@ -137,18 +150,46 @@ impl InterfacePrinter { } fn print_result_type(&mut self, interface: &Interface, result: &Result_) -> Result<()> { - self.output.push_str("result<"); - self.print_type_name(interface, &result.ok)?; - self.output.push_str(", "); - self.print_type_name(interface, &result.err)?; - self.output.push('>'); + match result { + Result_ { + ok: Some(ok), + err: Some(err), + } => { + self.output.push_str("result<"); + self.print_type_name(interface, &ok)?; + self.output.push_str(", "); + self.print_type_name(interface, &err)?; + self.output.push('>'); + } + Result_ { + ok: None, + err: Some(err), + } => { + self.output.push_str("result<_, "); + self.print_type_name(interface, &err)?; + self.output.push('>'); + } + Result_ { + ok: Some(ok), + err: None, + } => { + self.output.push_str("result<"); + self.print_type_name(interface, &ok)?; + self.output.push('>'); + } + Result_ { + ok: None, + err: None, + } => { + self.output.push_str("result"); + } + } Ok(()) } fn declare_type(&mut self, interface: &Interface, ty: &Type) -> Result<()> { match ty { - Type::Unit - | Type::Bool + Type::Bool | Type::U8 | Type::U16 | Type::U32 @@ -275,7 +316,9 @@ impl InterfacePrinter { variant: &Variant, ) -> Result<()> { for case in variant.cases.iter() { - self.declare_type(interface, &case.ty)?; + if let Some(ty) = case.ty { + self.declare_type(interface, &ty)?; + } } let name = match name { @@ -285,9 +328,9 @@ impl InterfacePrinter { writeln!(&mut self.output, "variant {} {{", name)?; for case in &variant.cases { write!(&mut self.output, " {}", case.name)?; - if case.ty != Type::Unit { + if let Some(ty) = case.ty { self.output.push('('); - self.print_type_name(interface, &case.ty)?; + self.print_type_name(interface, &ty)?; self.output.push(')'); } self.output.push_str(",\n"); @@ -342,8 +385,12 @@ impl InterfacePrinter { name: Option<&str>, result: &Result_, ) -> Result<()> { - self.declare_type(interface, &result.ok)?; - self.declare_type(interface, &result.err)?; + if let Some(ok) = result.ok { + self.declare_type(interface, &ok)?; + } + if let Some(err) = result.err { + self.declare_type(interface, &err)?; + } if let Some(name) = name { write!(&mut self.output, "type {} = ", name)?; diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs index c4267dcdf..8de99bfd0 100644 --- a/crates/wit-component/src/validation.rs +++ b/crates/wit-component/src/validation.rs @@ -37,20 +37,10 @@ fn wasm_sig_to_func_type(signature: WasmSignature) -> FuncType { } } - FuncType { - params: signature - .params - .iter() - .map(from_wasm_type) - .collect::>() - .into_boxed_slice(), - returns: signature - .results - .iter() - .map(from_wasm_type) - .collect::>() - .into_boxed_slice(), - } + FuncType::new( + signature.params.iter().map(from_wasm_type), + signature.results.iter().map(from_wasm_type), + ) } /// This function validates the following: @@ -197,10 +187,10 @@ fn validate_imported_interface( "type mismatch for function `{}` on imported interface `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`", func_name, name, - expected.params, - expected.returns, - ty.params, - ty.returns + expected.params(), + expected.results(), + ty.params(), + ty.results() ); } } @@ -227,18 +217,18 @@ fn validate_exported_interface( "type mismatch for function `{}` from exported interface `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`", f.name, name, - expected_ty.params, - expected_ty.returns, - ty.params, - ty.returns + expected_ty.params(), + expected_ty.results(), + ty.params(), + ty.results() ), None => bail!( "type mismatch for default interface function `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`", f.name, - expected_ty.params, - expected_ty.returns, - ty.params, - ty.returns + expected_ty.params(), + expected_ty.results(), + ty.params(), + ty.results() ) } } diff --git a/crates/wit-component/tests/components/exports/component.wat b/crates/wit-component/tests/components/exports/component.wat index af82b26a4..0954159cd 100644 --- a/crates/wit-component/tests/components/exports/component.wat +++ b/crates/wit-component/tests/components/exports/component.wat @@ -5,7 +5,7 @@ (type (;3;) (func (result 2))) (type (;4;) (flags "a" "b" "c")) (type (;5;) (func (param "x" 4))) - (type (;6;) (variant (case $c0 "a" unit) (case $c1 "b" string) (case $c2 "c" s64))) + (type (;6;) (variant (case "a") (case "b" string) (case "c" s64))) (type (;7;) (func (param "x" string) (result 6))) (type (;8;) (func (param "x" 6) (result string))) (core module (;0;) diff --git a/crates/wit-component/tests/components/lift-options/component.wat b/crates/wit-component/tests/components/lift-options/component.wat index 756ddd98f..b29813c49 100644 --- a/crates/wit-component/tests/components/lift-options/component.wat +++ b/crates/wit-component/tests/components/lift-options/component.wat @@ -4,11 +4,11 @@ (type (;2;) (func (param "x" 1))) (type (;3;) (record (field "s" string))) (type (;4;) (func (param "x" 3))) - (type (;5;) (variant (case $c0 "s" string))) + (type (;5;) (variant (case "s" string))) (type (;6;) (func (param "x" 5))) (type (;7;) (record (field "s" u32))) (type (;8;) (func (param "x" 7))) - (type (;9;) (variant (case $c0 "s" u32))) + (type (;9;) (variant (case "s" u32))) (type (;10;) (func (param "x" 9))) (type (;11;) (list 3)) (type (;12;) (func (param "x" 11))) diff --git a/crates/wit-component/tests/components/lower-options/component.wat b/crates/wit-component/tests/components/lower-options/component.wat index 7a04c2106..3f78e2a6c 100644 --- a/crates/wit-component/tests/components/lower-options/component.wat +++ b/crates/wit-component/tests/components/lower-options/component.wat @@ -4,11 +4,11 @@ (type (;2;) (func (param "x" 1))) (type (;3;) (record (field "s" string))) (type (;4;) (func (param "x" 3))) - (type (;5;) (variant (case $c0 "s" string))) + (type (;5;) (variant (case "s" string))) (type (;6;) (func (param "x" 5))) (type (;7;) (record (field "s" u32))) (type (;8;) (func (param "x" 7))) - (type (;9;) (variant (case $c0 "s" u32))) + (type (;9;) (variant (case "s" u32))) (type (;10;) (func (param "x" 9))) (type (;11;) (list 3)) (type (;12;) (func (param "x" 11))) diff --git a/crates/wit-component/tests/interfaces/lists/lists.wat b/crates/wit-component/tests/interfaces/lists/lists.wat index eade24582..fb58caeac 100644 --- a/crates/wit-component/tests/interfaces/lists/lists.wat +++ b/crates/wit-component/tests/interfaces/lists/lists.wat @@ -48,9 +48,9 @@ (type (;46;) (list 45)) (type (;47;) (list 44)) (type (;48;) (func (param "x" 46) (result 47))) - (type (;49;) (variant (case $c0 "a" unit) (case $c1 "b" u32) (case $c2 "c" string))) + (type (;49;) (variant (case "a") (case "b" u32) (case "c" string))) (type (;50;) (list 49)) - (type (;51;) (variant (case $c0 "a" string) (case $c1 "b" unit) (case $c2 "c" u32) (case $c3 "d" 50))) + (type (;51;) (variant (case "a" string) (case "b") (case "c" u32) (case "d" 50))) (type (;52;) (list 51)) (type (;53;) (func (param "x" 52) (result 50))) (type (;54;) (tuple string u8 s8 u16 s16 u32 s32 u64 s64 float32 float64 char)) diff --git a/crates/wit-component/tests/interfaces/variants/variants.wat b/crates/wit-component/tests/interfaces/variants/variants.wat index bcb545602..97db711af 100644 --- a/crates/wit-component/tests/interfaces/variants/variants.wat +++ b/crates/wit-component/tests/interfaces/variants/variants.wat @@ -6,7 +6,7 @@ (type (;4;) (func (param "x" 3))) (type (;5;) (func (result 3))) (type (;6;) (record)) - (type (;7;) (variant (case $c0 "a" unit) (case $c1 "b" 3) (case $c2 "c" 0) (case $c3 "d" string) (case $c4 "e" 6) (case $c5 "f" unit) (case $c6 "g" u32))) + (type (;7;) (variant (case "a") (case "b" 3) (case "c" 0) (case "d" string) (case "e" 6) (case "f") (case "g" u32))) (type (;8;) (func (param "x" 7))) (type (;9;) (func (result 7))) (type (;10;) (func (param "x" bool))) @@ -22,41 +22,41 @@ (type (;20;) (func (param "a" 12) (param "b" 14) (param "c" 15) (param "d" 16) (param "e" 17) (param "f" 18) (param "g" 19))) (type (;21;) (tuple 12 14 15 16 17 18 19)) (type (;22;) (func (result 21))) - (type (;23;) (variant (case $c0 "a" s32) (case $c1 "b" float32))) - (type (;24;) (variant (case $c0 "a" float64) (case $c1 "b" float32))) - (type (;25;) (variant (case $c0 "a" float64) (case $c1 "b" u64))) - (type (;26;) (variant (case $c0 "a" u32) (case $c1 "b" s64))) - (type (;27;) (variant (case $c0 "a" float32) (case $c1 "b" s64))) + (type (;23;) (variant (case "a" s32) (case "b" float32))) + (type (;24;) (variant (case "a" float64) (case "b" float32))) + (type (;25;) (variant (case "a" float64) (case "b" u64))) + (type (;26;) (variant (case "a" u32) (case "b" s64))) + (type (;27;) (variant (case "a" float32) (case "b" s64))) (type (;28;) (tuple float32 u32)) (type (;29;) (tuple u32 u32)) - (type (;30;) (variant (case $c0 "a" 28) (case $c1 "b" 29))) + (type (;30;) (variant (case "a" 28) (case "b" 29))) (type (;31;) (tuple 23 24 25 26 27 30)) (type (;32;) (func (param "a" 23) (param "b" 24) (param "c" 25) (param "d" 26) (param "e" 27) (param "f" 30) (result 31))) - (type (;33;) (expected unit unit)) - (type (;34;) (expected unit 0)) - (type (;35;) (expected 0 unit)) - (type (;36;) (expected 13 13)) - (type (;37;) (expected u32 7)) + (type (;33;) (result)) + (type (;34;) (result (error 0))) + (type (;35;) (result 0)) + (type (;36;) (result 13 (error 13))) + (type (;37;) (result u32 (error 7))) (type (;38;) (list u8)) - (type (;39;) (expected string 38)) + (type (;39;) (result string (error 38))) (type (;40;) (func (param "a" 33) (param "b" 34) (param "c" 35) (param "d" 36) (param "e" 37) (param "f" 39))) (type (;41;) (tuple 33 34 35 36 37 39)) (type (;42;) (func (result 41))) (type (;43;) (enum "bad1" "bad2")) - (type (;44;) (expected s32 43)) + (type (;44;) (result s32 (error 43))) (type (;45;) (func (result 44))) - (type (;46;) (expected unit 43)) + (type (;46;) (result (error 43))) (type (;47;) (func (result 46))) - (type (;48;) (expected 43 43)) + (type (;48;) (result 43 (error 43))) (type (;49;) (func (result 48))) (type (;50;) (tuple s32 u32)) - (type (;51;) (expected 50 43)) + (type (;51;) (result 50 (error 43))) (type (;52;) (func (result 51))) (type (;53;) (option s32)) (type (;54;) (func (result 53))) (type (;55;) (option 43)) (type (;56;) (func (result 55))) - (type (;57;) (expected u32 s32)) + (type (;57;) (result u32 (error s32))) (type (;58;) (func (result 57))) (export "e1" (type 0)) (export "e1-arg" (type 1)) diff --git a/crates/wit-component/tests/interfaces/variants/variants.wit b/crates/wit-component/tests/interfaces/variants/variants.wit index 34eb5791b..43bbfb9f0 100644 --- a/crates/wit-component/tests/interfaces/variants/variants.wit +++ b/crates/wit-component/tests/interfaces/variants/variants.wit @@ -77,13 +77,13 @@ option-result: func() -> tuple, option>, option, optio casts: func(a: casts1, b: casts2, c: casts3, d: casts4, e: casts5, f: casts6) -> tuple -expected-arg: func(a: result, b: result, c: result, d: result, tuple<>>, e: result, f: result>) +expected-arg: func(a: result, b: result<_, e1>, c: result, d: result, tuple<>>, e: result, f: result>) -expected-result: func() -> tuple, result, result, result, tuple<>>, result, result>> +expected-result: func() -> tuple, result, result, tuple<>>, result, result>> return-expected-sugar: func() -> result -return-expected-sugar2: func() -> result +return-expected-sugar2: func() -> result<_, my-errno> return-expected-sugar3: func() -> result diff --git a/crates/wit-parser/src/abi.rs b/crates/wit-parser/src/abi.rs index b6f70dbe9..97fb921a1 100644 --- a/crates/wit-parser/src/abi.rs +++ b/crates/wit-parser/src/abi.rs @@ -1,7 +1,7 @@ use crate::sizealign::align_to; use crate::{ - Enum, Flags, FlagsRepr, Function, Int, Interface, Record, ResourceId, Result_, Tuple, Type, - TypeDefKind, TypeId, Union, Variant, + Enum, Flags, FlagsRepr, Function, Int, Interface, Record, ResourceId, Result_, Results, Tuple, + Type, TypeDefKind, TypeId, Union, Variant, }; /// A raw WebAssembly signature with params and results. @@ -267,11 +267,6 @@ def_instruction! { /// Creates an `i32` from a `bool` input, must return 0 or 1. I32FromBool : [1] => [1], - /// Creates a "unit" value from nothing. - UnitLift : [0] => [1], - /// Consumes a "unit" value and returns nothing. - UnitLower : [1] => [0], - // Handles /// Converts a "borrowed" handle into a wasm `i32` value. @@ -492,7 +487,7 @@ def_instruction! { /// This is used for both lifting and lowering lists. IterBasePointer : [0] => [1], - // records + // records and tuples /// Pops a record value off the stack, decomposes the record to all of /// its fields, and then pushes the fields onto the stack. @@ -648,7 +643,7 @@ def_instruction! { CallInterface { module: &'a str, func: &'a Function, - } : [func.params.len()] => [1], + } : [func.params.len()] => [func.results.len()], /// Returns `amt` values on the stack. This is always the last /// instruction. @@ -824,7 +819,9 @@ impl Interface { } let mut results = Vec::new(); - self.push_wasm(variant, &func.result, &mut results); + for ty in func.results.iter_types() { + self.push_wasm(variant, ty, &mut results) + } let mut retptr = false; @@ -855,8 +852,6 @@ impl Interface { fn push_wasm(&self, variant: AbiVariant, ty: &Type, result: &mut Vec) { match ty { - Type::Unit => {} - Type::Bool | Type::S8 | Type::U8 @@ -903,24 +898,24 @@ impl Interface { TypeDefKind::Variant(v) => { result.push(v.tag().into()); - self.push_wasm_variants(variant, v.cases.iter().map(|c| &c.ty), result); + self.push_wasm_variants(variant, v.cases.iter().map(|c| c.ty.as_ref()), result); } TypeDefKind::Enum(e) => result.push(e.tag().into()), TypeDefKind::Option(t) => { result.push(WasmType::I32); - self.push_wasm_variants(variant, [&Type::Unit, t], result); + self.push_wasm_variants(variant, [None, Some(t)], result); } TypeDefKind::Result(r) => { result.push(WasmType::I32); - self.push_wasm_variants(variant, [&r.ok, &r.err], result); + self.push_wasm_variants(variant, [r.ok.as_ref(), r.err.as_ref()], result); } TypeDefKind::Union(u) => { result.push(WasmType::I32); - self.push_wasm_variants(variant, u.cases.iter().map(|c| &c.ty), result); + self.push_wasm_variants(variant, u.cases.iter().map(|c| Some(&c.ty)), result); } TypeDefKind::Future(_) => { @@ -937,7 +932,7 @@ impl Interface { fn push_wasm_variants<'a>( &self, variant: AbiVariant, - tys: impl IntoIterator, + tys: impl IntoIterator>, result: &mut Vec, ) { let mut temp = Vec::new(); @@ -950,12 +945,14 @@ impl Interface { // f32>` where that turns into `[i32 i32]` where the second // `i32` might be the `f32` bitcasted. for ty in tys { - self.push_wasm(variant, ty, &mut temp); + if let Some(ty) = ty { + self.push_wasm(variant, ty, &mut temp); - for (i, ty) in temp.drain(..).enumerate() { - match result.get_mut(start + i) { - Some(prev) => *prev = join(*prev, ty), - None => result.push(ty), + for (i, ty) in temp.drain(..).enumerate() { + match result.get_mut(start + i) { + Some(prev) => *prev = join(*prev, ty), + None => result.push(ty), + } } } } @@ -1069,8 +1066,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // If necessary we may need to prepare a return pointer for // this ABI. if self.variant == AbiVariant::GuestImport && sig.retptr { - let size = self.bindgen.sizes().size(&func.result); - let align = self.bindgen.sizes().align(&func.result); + let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); let ptr = self.bindgen.return_pointer(self.iface, size, align); self.return_pointer = Some(ptr.clone()); self.stack.push(ptr); @@ -1088,9 +1084,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { if !sig.retptr { // With no return pointer in use we can simply lift the - // result of the function from the result of the core + // result(s) of the function from the result of the core // wasm function. - self.lift(&func.result); + for ty in func.results.iter_types() { + self.lift(ty) + } } else { let ptr = match self.variant { // imports into guests means it's a wasm module @@ -1109,10 +1107,13 @@ impl<'a, B: Bindgen> Generator<'a, B> { AbiVariant::GuestExport => self.stack.pop().unwrap(), }; - self.read_from_memory(&func.result, ptr, 0); + self.read_results_from_memory(&func.results, ptr, 0); } - self.emit(&Instruction::Return { func, amt: 1 }); + self.emit(&Instruction::Return { + func, + amt: func.results.len(), + }); } LiftLower::LiftArgsLowerResults => { if !sig.indirect_params { @@ -1169,8 +1170,15 @@ impl<'a, B: Bindgen> Generator<'a, B> { if !sig.retptr { // With no return pointer in use we simply lower the - // result and return that directly from the function. - self.lower(&func.result); + // result(s) and return that directly from the function. + let results = self + .stack + .drain(self.stack.len() - func.results.len()..) + .collect::>(); + for (ty, result) in func.results.iter_types().zip(results) { + self.stack.push(result); + self.lower(ty); + } } else { match self.variant { // When a function is imported to a guest this means @@ -1184,7 +1192,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { nth: sig.params.len() - 1, }); let ptr = self.stack.pop().unwrap(); - self.write_to_memory(&func.result, ptr, 0); + self.write_params_to_memory(func.results.iter_types(), ptr, 0); } // For a guest import this is a function defined in @@ -1193,10 +1201,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { // (statically) and then write the result into that // memory, returning the pointer at the end. AbiVariant::GuestExport => { - let size = self.bindgen.sizes().size(&func.result); - let align = self.bindgen.sizes().align(&func.result); + let (size, align) = + self.bindgen.sizes().params(func.results.iter_types()); let ptr = self.bindgen.return_pointer(self.iface, size, align); - self.write_to_memory(&func.result, ptr.clone(), 0); + self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); self.stack.push(ptr); } } @@ -1263,7 +1271,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { use Instruction::*; match *ty { - Type::Unit => self.emit(&UnitLower), Type::Bool => self.emit(&I32FromBool), Type::S8 => self.emit(&I32FromS8), Type::U8 => self.emit(&I32FromU8), @@ -1366,7 +1373,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { } TypeDefKind::Variant(v) => { - let results = self.lower_variant_arms(ty, v.cases.iter().map(|c| &c.ty)); + let results = + self.lower_variant_arms(ty, v.cases.iter().map(|c| c.ty.as_ref())); self.emit(&VariantLower { variant: v, ty: id, @@ -1382,7 +1390,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } TypeDefKind::Option(t) => { - let results = self.lower_variant_arms(ty, [&Type::Unit, t]); + let results = self.lower_variant_arms(ty, [None, Some(t)]); self.emit(&OptionLower { payload: t, ty: id, @@ -1390,7 +1398,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } TypeDefKind::Result(r) => { - let results = self.lower_variant_arms(ty, [&r.ok, &r.err]); + let results = self.lower_variant_arms(ty, [r.ok.as_ref(), r.err.as_ref()]); self.emit(&ResultLower { result: r, ty: id, @@ -1398,7 +1406,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } TypeDefKind::Union(union) => { - let results = self.lower_variant_arms(ty, union.cases.iter().map(|c| &c.ty)); + let results = + self.lower_variant_arms(ty, union.cases.iter().map(|c| Some(&c.ty))); self.emit(&UnionLower { union, ty: id, @@ -1415,7 +1424,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { fn lower_variant_arms<'b>( &mut self, ty: &Type, - cases: impl IntoIterator, + cases: impl IntoIterator>, ) -> Vec { use Instruction::*; let mut results = Vec::new(); @@ -1428,28 +1437,30 @@ impl<'a, B: Bindgen> Generator<'a, B> { let payload_name = self.stack.pop().unwrap(); self.emit(&I32Const { val: i as i32 }); let mut pushed = 1; - // Using the payload of this block we lower the type to - // raw wasm values. - self.stack.push(payload_name.clone()); - self.lower(ty); - - // Determine the types of all the wasm values we just - // pushed, and record how many. If we pushed too few - // then we'll need to push some zeros after this. - temp.truncate(0); - self.iface.push_wasm(self.variant, ty, &mut temp); - pushed += temp.len(); - - // For all the types pushed we may need to insert some - // bitcasts. This will go through and cast everything - // to the right type to ensure all blocks produce the - // same set of results. - casts.truncate(0); - for (actual, expected) in temp.iter().zip(&results[1..]) { - casts.push(cast(*actual, *expected)); - } - if casts.iter().any(|c| *c != Bitcast::None) { - self.emit(&Bitcasts { casts: &casts }); + if let Some(ty) = ty { + // Using the payload of this block we lower the type to + // raw wasm values. + self.stack.push(payload_name); + self.lower(ty); + + // Determine the types of all the wasm values we just + // pushed, and record how many. If we pushed too few + // then we'll need to push some zeros after this. + temp.truncate(0); + self.iface.push_wasm(self.variant, ty, &mut temp); + pushed += temp.len(); + + // For all the types pushed we may need to insert some + // bitcasts. This will go through and cast everything + // to the right type to ensure all blocks produce the + // same set of results. + casts.truncate(0); + for (actual, expected) in temp.iter().zip(&results[1..]) { + casts.push(cast(*actual, *expected)); + } + if casts.iter().any(|c| *c != Bitcast::None) { + self.emit(&Bitcasts { casts: &casts }); + } } // If we haven't pushed enough items in this block to match @@ -1481,7 +1492,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { use Instruction::*; match *ty { - Type::Unit => self.emit(&UnitLift), Type::Bool => self.emit(&BoolFromI32), Type::S8 => self.emit(&S8FromI32), Type::U8 => self.emit(&U8FromI32), @@ -1578,7 +1588,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } TypeDefKind::Variant(v) => { - self.lift_variant_arms(ty, v.cases.iter().map(|c| &c.ty)); + self.lift_variant_arms(ty, v.cases.iter().map(|c| c.ty.as_ref())); self.emit(&VariantLift { variant: v, ty: id, @@ -1595,17 +1605,17 @@ impl<'a, B: Bindgen> Generator<'a, B> { } TypeDefKind::Option(t) => { - self.lift_variant_arms(ty, [&Type::Unit, t]); + self.lift_variant_arms(ty, [None, Some(t)]); self.emit(&OptionLift { payload: t, ty: id }); } TypeDefKind::Result(r) => { - self.lift_variant_arms(ty, [&r.ok, &r.err]); + self.lift_variant_arms(ty, [r.ok.as_ref(), r.err.as_ref()]); self.emit(&ResultLift { result: r, ty: id }); } TypeDefKind::Union(union) => { - self.lift_variant_arms(ty, union.cases.iter().map(|c| &c.ty)); + self.lift_variant_arms(ty, union.cases.iter().map(|c| Some(&c.ty))); self.emit(&UnionLift { union, ty: id, @@ -1619,7 +1629,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - fn lift_variant_arms<'b>(&mut self, ty: &Type, cases: impl IntoIterator) { + fn lift_variant_arms<'b>( + &mut self, + ty: &Type, + cases: impl IntoIterator>, + ) { let mut params = Vec::new(); let mut temp = Vec::new(); let mut casts = Vec::new(); @@ -1630,26 +1644,28 @@ impl<'a, B: Bindgen> Generator<'a, B> { .collect::>(); for ty in cases { self.push_block(); - // Push only the values we need for this variant onto - // the stack. - temp.truncate(0); - self.iface.push_wasm(self.variant, ty, &mut temp); - self.stack - .extend(block_inputs[..temp.len()].iter().cloned()); - - // Cast all the types we have on the stack to the actual - // types needed for this variant, if necessary. - casts.truncate(0); - for (actual, expected) in temp.iter().zip(¶ms[1..]) { - casts.push(cast(*expected, *actual)); - } - if casts.iter().any(|c| *c != Bitcast::None) { - self.emit(&Instruction::Bitcasts { casts: &casts }); - } + if let Some(ty) = ty { + // Push only the values we need for this variant onto + // the stack. + temp.truncate(0); + self.iface.push_wasm(self.variant, ty, &mut temp); + self.stack + .extend(block_inputs[..temp.len()].iter().cloned()); + + // Cast all the types we have on the stack to the actual + // types needed for this variant, if necessary. + casts.truncate(0); + for (actual, expected) in temp.iter().zip(¶ms[1..]) { + casts.push(cast(*expected, *actual)); + } + if casts.iter().any(|c| *c != Bitcast::None) { + self.emit(&Instruction::Bitcasts { casts: &casts }); + } - // Then recursively lift this variant's payload. - self.lift(ty); - self.finish_block(1); + // Then recursively lift this variant's payload. + self.lift(ty); + } + self.finish_block(ty.is_some() as usize); } } @@ -1667,7 +1683,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { use Instruction::*; match *ty { - Type::Unit => self.lower(ty), // Builtin types need different flavors of storage instructions // depending on the size of the value written. Type::Bool | Type::U8 | Type::S8 => { @@ -1694,15 +1709,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { ty: id, name: self.iface.types[id].name.as_deref().unwrap(), }); - self.write_fields_to_memory( - &record.fields.iter().map(|f| f.ty).collect::>(), - addr, - offset, - ); + self.write_fields_to_memory(record.fields.iter().map(|f| &f.ty), addr, offset); } TypeDefKind::Tuple(tuple) => { self.emit(&TupleLower { tuple, ty: id }); - self.write_fields_to_memory(&tuple.types, addr, offset); + self.write_fields_to_memory(tuple.types.iter(), addr, offset); } TypeDefKind::Flags(f) => { @@ -1736,7 +1747,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { offset, addr, v.tag(), - v.cases.iter().map(|c| &c.ty), + v.cases.iter().map(|c| c.ty.as_ref()), ); self.emit(&VariantLower { variant: v, @@ -1747,7 +1758,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } TypeDefKind::Option(t) => { - self.write_variant_arms_to_memory(offset, addr, Int::U8, [&Type::Unit, t]); + self.write_variant_arms_to_memory(offset, addr, Int::U8, [None, Some(t)]); self.emit(&OptionLower { payload: t, ty: id, @@ -1756,7 +1767,12 @@ impl<'a, B: Bindgen> Generator<'a, B> { } TypeDefKind::Result(r) => { - self.write_variant_arms_to_memory(offset, addr, Int::U8, [&r.ok, &r.err]); + self.write_variant_arms_to_memory( + offset, + addr, + Int::U8, + [r.ok.as_ref(), r.err.as_ref()], + ); self.emit(&ResultLower { result: r, ty: id, @@ -1775,7 +1791,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { offset, addr, union.tag(), - union.cases.iter().map(|c| &c.ty), + union.cases.iter().map(|c| Some(&c.ty)), ); self.emit(&UnionLower { union, @@ -1791,12 +1807,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } + fn write_params_to_memory<'b>( + &mut self, + params: impl IntoIterator + ExactSizeIterator, + addr: B::Operand, + offset: i32, + ) { + self.write_fields_to_memory(params, addr, offset); + } + fn write_variant_arms_to_memory<'b>( &mut self, offset: i32, addr: B::Operand, tag: Int, - cases: impl IntoIterator + Clone, + cases: impl IntoIterator> + Clone, ) { let payload_offset = offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()) as i32); @@ -1807,8 +1832,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&Instruction::I32Const { val: i as i32 }); self.stack.push(addr.clone()); self.store_intrepr(offset, tag); - self.stack.push(payload_name.clone()); - self.write_to_memory(ty, addr.clone(), payload_offset); + if let Some(ty) = ty { + self.stack.push(payload_name.clone()); + self.write_to_memory(ty, addr.clone(), payload_offset); + } self.finish_block(0); } } @@ -1824,18 +1851,22 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&Instruction::I32Store { offset }); } - fn write_fields_to_memory(&mut self, tys: &[Type], addr: B::Operand, offset: i32) { + fn write_fields_to_memory<'b>( + &mut self, + tys: impl IntoIterator + ExactSizeIterator, + addr: B::Operand, + offset: i32, + ) { let fields = self .stack .drain(self.stack.len() - tys.len()..) .collect::>(); - for ((field_offset, op), ty) in self + for ((field_offset, ty), op) in self .bindgen .sizes() - .field_offsets(tys.iter()) + .field_offsets(tys) .into_iter() .zip(fields) - .zip(tys) { self.stack.push(op); self.write_to_memory(ty, addr.clone(), offset + (field_offset as i32)); @@ -1852,7 +1883,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { use Instruction::*; match *ty { - Type::Unit => self.emit(&UnitLift), Type::Bool => self.emit_and_lift(ty, addr, &I32Load8U { offset }), Type::U8 => self.emit_and_lift(ty, addr, &I32Load8U { offset }), Type::S8 => self.emit_and_lift(ty, addr, &I32Load8S { offset }), @@ -1875,11 +1905,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // as we go along, then aggregate all the fields into the // record. TypeDefKind::Record(record) => { - self.read_fields_from_memory( - &record.fields.iter().map(|f| f.ty).collect::>(), - addr, - offset, - ); + self.read_fields_from_memory(record.fields.iter().map(|f| &f.ty), addr, offset); self.emit(&RecordLift { record, ty: id, @@ -1922,7 +1948,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { offset, addr, variant.tag(), - variant.cases.iter().map(|c| &c.ty), + variant.cases.iter().map(|c| c.ty.as_ref()), ); self.emit(&VariantLift { variant, @@ -1932,12 +1958,17 @@ impl<'a, B: Bindgen> Generator<'a, B> { } TypeDefKind::Option(t) => { - self.read_variant_arms_from_memory(offset, addr, Int::U8, [&Type::Unit, t]); + self.read_variant_arms_from_memory(offset, addr, Int::U8, [None, Some(t)]); self.emit(&OptionLift { payload: t, ty: id }); } TypeDefKind::Result(r) => { - self.read_variant_arms_from_memory(offset, addr, Int::U8, [&r.ok, &r.err]); + self.read_variant_arms_from_memory( + offset, + addr, + Int::U8, + [r.ok.as_ref(), r.err.as_ref()], + ); self.emit(&ResultLift { result: r, ty: id }); } @@ -1952,7 +1983,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { offset, addr, union.tag(), - union.cases.iter().map(|c| &c.ty), + union.cases.iter().map(|c| Some(&c.ty)), ); self.emit(&UnionLift { union, @@ -1967,12 +1998,16 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } + fn read_results_from_memory(&mut self, results: &Results, addr: B::Operand, offset: i32) { + self.read_fields_from_memory(results.iter_types(), addr, offset) + } + fn read_variant_arms_from_memory<'b>( &mut self, offset: i32, addr: B::Operand, tag: Int, - cases: impl IntoIterator + Clone, + cases: impl IntoIterator> + Clone, ) { self.stack.push(addr.clone()); self.load_intrepr(offset, tag); @@ -1980,8 +2015,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()) as i32); for ty in cases { self.push_block(); - self.read_from_memory(ty, addr.clone(), payload_offset); - self.finish_block(1); + if let Some(ty) = ty { + self.read_from_memory(ty, addr.clone(), payload_offset); + } + self.finish_block(ty.is_some() as usize); } } @@ -1995,9 +2032,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lift(ty); } - fn read_fields_from_memory(&mut self, tys: &[Type], addr: B::Operand, offset: i32) { - for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys).into_iter().zip(tys) { - self.read_from_memory(ty, addr.clone(), offset + (field_offset as i32)); + fn read_fields_from_memory<'b>( + &mut self, + tys: impl IntoIterator, + addr: B::Operand, + offset: i32, + ) { + for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys).iter() { + self.read_from_memory(ty, addr.clone(), offset + (*field_offset as i32)); } } diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index 2f9a8b5a2..219a6496b 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -74,7 +74,6 @@ pub struct TypeDef<'a> { } enum Type<'a> { - Unit, Bool, U8, U16, @@ -98,7 +97,7 @@ enum Type<'a> { Enum(Enum<'a>), Option(Box>), Result(Result_<'a>), - Future(Box>), + Future(Option>>), Stream(Stream<'a>), Union(Union<'a>), } @@ -144,13 +143,13 @@ struct EnumCase<'a> { } struct Result_<'a> { - ok: Box>, - err: Box>, + ok: Option>>, + err: Option>>, } struct Stream<'a> { - element: Box>, - end: Box>, + element: Option>>, + end: Option>>, } pub struct Value<'a> { @@ -169,10 +168,17 @@ struct UnionCase<'a> { ty: Type<'a>, } +type ParamList<'a> = Vec<(Id<'a>, Type<'a>)>; + +enum ResultList<'a> { + Named(ParamList<'a>), + Anon(Type<'a>), +} + enum ValueKind<'a> { Function { - params: Vec<(Id<'a>, Type<'a>)>, - result: Type<'a>, + params: ParamList<'a>, + results: ResultList<'a>, }, Global(Type<'a>), } @@ -414,24 +420,34 @@ impl<'a> Value<'a> { }; return Ok(Value { docs, name, kind }); + fn parse_params<'a>(tokens: &mut Tokenizer<'a>, left_paren: bool) -> Result> { + if left_paren { + tokens.expect(Token::LeftParen)?; + }; + parse_list_trailer(tokens, Token::RightParen, |_docs, tokens| { + let name = parse_id(tokens)?; + tokens.expect(Token::Colon)?; + let ty = Type::parse(tokens)?; + Ok((name, ty)) + }) + } + fn parse_func<'a>(tokens: &mut Tokenizer<'a>) -> Result> { - let params = parse_list( - tokens, - Token::LeftParen, - Token::RightParen, - |_docs, tokens| { - let name = parse_id(tokens)?; - tokens.expect(Token::Colon)?; + let params = parse_params(tokens, true)?; + let results = if tokens.eat(Token::RArrow)? { + // If we eat a '(', parse the remainder of the named + // result types. Otherwise parse a single anonymous type. + if tokens.eat(Token::LeftParen)? { + let results = parse_params(tokens, false)?; + ResultList::Named(results) + } else { let ty = Type::parse(tokens)?; - Ok((name, ty)) - }, - )?; - let result = if tokens.eat(Token::RArrow)? { - Type::parse(tokens)? + ResultList::Anon(ty) + } } else { - Type::Unit + ResultList::Named(Vec::new()) }; - Ok(ValueKind::Function { params, result }) + Ok(ValueKind::Function { params, results }) } } } @@ -494,7 +510,6 @@ impl<'a> Type<'a> { Ok(Type::Tuple(types)) } - Some((_span, Token::Unit)) => Ok(Type::Unit), Some((_span, Token::Bool)) => Ok(Type::Bool), Some((_span, Token::String_)) => Ok(Type::String), @@ -515,30 +530,60 @@ impl<'a> Type<'a> { } // result + // result<_, E> + // result + // result Some((_span, Token::Result_)) => { - tokens.expect(Token::LessThan)?; - let ok = Box::new(Type::parse(tokens)?); - tokens.expect(Token::Comma)?; - let err = Box::new(Type::parse(tokens)?); - tokens.expect(Token::GreaterThan)?; + let mut ok = None; + let mut err = None; + + if tokens.eat(Token::LessThan)? { + if tokens.eat(Token::Underscore)? { + tokens.expect(Token::Comma)?; + err = Some(Box::new(Type::parse(tokens)?)); + } else { + ok = Some(Box::new(Type::parse(tokens)?)); + if tokens.eat(Token::Comma)? { + err = Some(Box::new(Type::parse(tokens)?)); + } + }; + tokens.expect(Token::GreaterThan)?; + }; Ok(Type::Result(Result_ { ok, err })) } // future + // future Some((_span, Token::Future)) => { - tokens.expect(Token::LessThan)?; - let ty = Box::new(Type::parse(tokens)?); - tokens.expect(Token::GreaterThan)?; + let mut ty = None; + + if tokens.eat(Token::LessThan)? { + ty = Some(Box::new(Type::parse(tokens)?)); + tokens.expect(Token::GreaterThan)?; + }; Ok(Type::Future(ty)) } // stream + // stream<_, Z> + // stream + // stream Some((_span, Token::Stream)) => { - tokens.expect(Token::LessThan)?; - let element = Box::new(Type::parse(tokens)?); - tokens.expect(Token::Comma)?; - let end = Box::new(Type::parse(tokens)?); - tokens.expect(Token::GreaterThan)?; + let mut element = None; + let mut end = None; + + if tokens.eat(Token::LessThan)? { + if tokens.eat(Token::Underscore)? { + tokens.expect(Token::Comma)?; + end = Some(Box::new(Type::parse(tokens)?)); + } else { + element = Some(Box::new(Type::parse(tokens)?)); + if tokens.eat(Token::Comma)? { + end = Some(Box::new(Type::parse(tokens)?)); + } + }; + tokens.expect(Token::GreaterThan)?; + }; Ok(Type::Stream(Stream { element, end })) } @@ -579,9 +624,17 @@ fn parse_list<'a, T>( tokens: &mut Tokenizer<'a>, start: Token, end: Token, - mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> Result, + parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> Result, ) -> Result> { tokens.expect(start)?; + parse_list_trailer(tokens, end, parse) +} + +fn parse_list_trailer<'a, T>( + tokens: &mut Tokenizer<'a>, + end: Token, + mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> Result, +) -> Result> { let mut items = Vec::new(); loop { // get docs before we skip them to try to eat the end token diff --git a/crates/wit-parser/src/ast/lex.rs b/crates/wit-parser/src/ast/lex.rs index 4b9abef97..7537ea69b 100644 --- a/crates/wit-parser/src/ast/lex.rs +++ b/crates/wit-parser/src/ast/lex.rs @@ -80,7 +80,6 @@ pub enum Token { Static, Interface, Tuple, - Unit, Implements, Id, @@ -270,7 +269,6 @@ impl<'a> Tokenizer<'a> { "static" => Static, "interface" => Interface, "tuple" => Tuple, - "unit" => Unit, "implements" => Implements, _ => Id, } @@ -538,7 +536,6 @@ impl Token { Static => "keyword `static`", Interface => "keyword `interface`", Tuple => "keyword `tuple`", - Unit => "keyword `unit`", Implements => "keyword `implements`", } } diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index daef51394..be061362d 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -1,4 +1,4 @@ -use super::{Error, Item, Span, Value, ValueKind}; +use super::{Error, Item, ParamList, ResultList, Span, Value, ValueKind}; use crate::*; use anyhow::Result; use std::collections::{HashMap, HashSet}; @@ -19,17 +19,17 @@ pub struct Resolver { #[derive(PartialEq, Eq, Hash)] enum Key { - Variant(Vec<(String, Type)>), + Variant(Vec<(String, Option)>), Record(Vec<(String, Type)>), Flags(Vec), Tuple(Vec), Enum(Vec), List(Type), Option(Type), - Result(Type, Type), + Result(Option, Option), Union(Vec), - Future(Type), - Stream(Type, Type), + Future(Option), + Stream(Option, Option), } impl Resolver { @@ -228,7 +228,7 @@ impl Resolver { .map(|case| Case { docs: case.docs.clone(), name: case.name.clone(), - ty: self.copy_type(dep_name, dep, case.ty), + ty: self.copy_optional_type(dep_name, dep, case.ty), }) .collect(), }), @@ -238,8 +238,8 @@ impl Resolver { TypeDefKind::List(t) => TypeDefKind::List(self.copy_type(dep_name, dep, *t)), TypeDefKind::Option(t) => TypeDefKind::Option(self.copy_type(dep_name, dep, *t)), TypeDefKind::Result(r) => TypeDefKind::Result(Result_ { - ok: self.copy_type(dep_name, dep, r.ok), - err: self.copy_type(dep_name, dep, r.err), + ok: self.copy_optional_type(dep_name, dep, r.ok), + err: self.copy_optional_type(dep_name, dep, r.err), }), TypeDefKind::Union(u) => TypeDefKind::Union(Union { cases: u @@ -251,10 +251,12 @@ impl Resolver { }) .collect(), }), - TypeDefKind::Future(t) => TypeDefKind::Future(self.copy_type(dep_name, dep, *t)), + TypeDefKind::Future(t) => { + TypeDefKind::Future(self.copy_optional_type(dep_name, dep, *t)) + } TypeDefKind::Stream(e) => TypeDefKind::Stream(Stream { - element: self.copy_type(dep_name, dep, e.element), - end: self.copy_type(dep_name, dep, e.end), + element: self.copy_optional_type(dep_name, dep, e.element), + end: self.copy_optional_type(dep_name, dep, e.end), }), }, }; @@ -271,6 +273,19 @@ impl Resolver { } } + fn copy_optional_type( + &mut self, + dep_name: &str, + dep: &Interface, + ty: Option, + ) -> Option { + match ty { + Some(Type::Id(id)) => Some(Type::Id(self.copy_type_def(dep_name, dep, id))), + Some(Type::Handle(id)) => Some(Type::Handle(self.copy_resource(dep_name, dep, id))), + other => other, + } + } + fn register_names(&mut self, fields: &[Item<'_>]) -> Result<()> { let mut values = HashSet::new(); for field in fields { @@ -351,7 +366,6 @@ impl Resolver { fn resolve_type_def(&mut self, ty: &super::Type<'_>) -> Result { Ok(match ty { - super::Type::Unit => TypeDefKind::Type(Type::Unit), super::Type::Bool => TypeDefKind::Type(Type::Bool), super::Type::U8 => TypeDefKind::Type(Type::U8), super::Type::U16 => TypeDefKind::Type(Type::U16), @@ -442,10 +456,7 @@ impl Resolver { Ok(Case { docs: self.docs(&case.docs), name: case.name.name.to_string(), - ty: match &case.ty { - Some(ty) => self.resolve_type(ty)?, - None => Type::Unit, - }, + ty: self.resolve_optional_type(case.ty.as_ref())?, }) }) .collect::>>()?; @@ -473,8 +484,8 @@ impl Resolver { } super::Type::Option(ty) => TypeDefKind::Option(self.resolve_type(ty)?), super::Type::Result(r) => TypeDefKind::Result(Result_ { - ok: self.resolve_type(&r.ok)?, - err: self.resolve_type(&r.err)?, + ok: self.resolve_optional_type(r.ok.as_deref())?, + err: self.resolve_optional_type(r.err.as_deref())?, }), super::Type::Union(e) => { if e.cases.is_empty() { @@ -496,10 +507,12 @@ impl Resolver { .collect::>>()?; TypeDefKind::Union(Union { cases }) } - super::Type::Future(t) => TypeDefKind::Future(self.resolve_type(t)?), + super::Type::Future(t) => { + TypeDefKind::Future(self.resolve_optional_type(t.as_deref())?) + } super::Type::Stream(s) => TypeDefKind::Stream(Stream { - element: self.resolve_type(&s.element)?, - end: self.resolve_type(&s.end)?, + element: self.resolve_optional_type(s.element.as_deref())?, + end: self.resolve_optional_type(s.end.as_deref())?, }), }) } @@ -514,6 +527,21 @@ impl Resolver { })) } + fn resolve_optional_type(&mut self, ty: Option<&super::Type<'_>>) -> Result> { + match ty { + Some(ty) => { + let kind = self.resolve_type_def(ty)?; + Ok(Some(self.anon_type_def(TypeDef { + kind, + name: None, + docs: Docs::default(), + foreign_module: None, + }))) + } + None => Ok(None), + } + } + fn anon_type_def(&mut self, ty: TypeDef) -> Type { let key = match &ty.kind { TypeDefKind::Type(t) => return *t, @@ -579,18 +607,15 @@ impl Resolver { fn resolve_value(&mut self, value: &Value<'_>) -> Result<()> { let docs = self.docs(&value.docs); match &value.kind { - ValueKind::Function { params, result } => { - let params = params - .iter() - .map(|(name, ty)| Ok((name.name.to_string(), self.resolve_type(ty)?))) - .collect::>()?; - let result = self.resolve_type(result)?; + ValueKind::Function { params, results } => { + let params = self.resolve_params(params)?; + let results = self.resolve_results(results)?; self.functions.push(Function { docs, name: value.name.name.to_string(), kind: FunctionKind::Freestanding, params, - result, + results, }); } ValueKind::Global(ty) => { @@ -605,12 +630,26 @@ impl Resolver { Ok(()) } + fn resolve_params(&mut self, params: &ParamList<'_>) -> Result { + params + .iter() + .map(|(name, ty)| Ok((name.name.to_string(), self.resolve_type(ty)?))) + .collect::>() + } + + fn resolve_results(&mut self, results: &ResultList<'_>) -> Result { + match results { + ResultList::Named(rs) => Ok(Results::Named(self.resolve_params(rs)?)), + ResultList::Anon(ty) => Ok(Results::Anon(self.resolve_type(ty)?)), + } + } + fn resolve_resource(&mut self, resource: &super::Resource<'_>) -> Result<()> { let mut names = HashSet::new(); let id = self.resource_lookup[&*resource.name.name]; for (statik, value) in resource.values.iter() { - let (params, result) = match &value.kind { - ValueKind::Function { params, result } => (params, result), + let (params, results) = match &value.kind { + ValueKind::Function { params, results } => (params, results), ValueKind::Global(_) => { return Err(Error { span: value.name.span, @@ -627,11 +666,8 @@ impl Resolver { .into()); } let docs = self.docs(&value.docs); - let mut params = params - .iter() - .map(|(name, ty)| Ok((name.name.to_string(), self.resolve_type(ty)?))) - .collect::>>()?; - let result = self.resolve_type(result)?; + let mut params = self.resolve_params(params)?; + let results = self.resolve_results(results)?; let kind = if *statik { FunctionKind::Static { resource: id, @@ -649,7 +685,7 @@ impl Resolver { name: format!("{}::{}", resource.name.name, value.name.name), kind, params, - result, + results, }); } Ok(()) @@ -679,7 +715,7 @@ impl Resolver { } TypeDefKind::Variant(v) => { for case in v.cases.iter() { - if let Type::Id(id) = case.ty { + if let Some(Type::Id(id)) = case.ty { self.validate_type_not_recursive(span, id, visiting, valid)?; } } @@ -705,23 +741,23 @@ impl Resolver { } } TypeDefKind::Result(r) => { - if let Type::Id(id) = r.ok { + if let Some(Type::Id(id)) = r.ok { self.validate_type_not_recursive(span, id, visiting, valid)? } - if let Type::Id(id) = r.err { + if let Some(Type::Id(id)) = r.err { self.validate_type_not_recursive(span, id, visiting, valid)? } } TypeDefKind::Future(t) => { - if let Type::Id(id) = *t { + if let Some(Type::Id(id)) = *t { self.validate_type_not_recursive(span, id, visiting, valid)? } } TypeDefKind::Stream(s) => { - if let Type::Id(id) = s.element { + if let Some(Type::Id(id)) = s.element { self.validate_type_not_recursive(span, id, visiting, valid)? } - if let Type::Id(id) = s.end { + if let Some(Type::Id(id)) = s.end { self.validate_type_not_recursive(span, id, visiting, valid)? } } diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index 48f991c10..7de26c382 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -63,14 +63,13 @@ pub enum TypeDefKind { Result(Result_), Union(Union), List(Type), - Future(Type), + Future(Option), Stream(Stream), Type(Type), } #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum Type { - Unit, Bool, U8, U16, @@ -160,7 +159,7 @@ pub struct Variant { pub struct Case { pub docs: Docs, pub name: String, - pub ty: Type, + pub ty: Option, } impl Variant { @@ -198,8 +197,8 @@ impl Enum { #[derive(Debug, Clone, PartialEq)] pub struct Result_ { - pub ok: Type, - pub err: Type, + pub ok: Option, + pub err: Option, } #[derive(Debug, Clone, PartialEq)] @@ -226,8 +225,8 @@ impl Union { #[derive(Debug, Clone, PartialEq)] pub struct Stream { - pub element: Type, - pub end: Type, + pub element: Option, + pub end: Option, } #[derive(Clone, Default, Debug, PartialEq)] @@ -252,13 +251,67 @@ pub struct Global { pub ty: Type, } +pub type Params = Vec<(String, Type)>; + +#[derive(Debug, Clone, PartialEq)] +pub enum Results { + Named(Params), + Anon(Type), +} + +pub enum ResultsTypeIter<'a> { + Named(std::slice::Iter<'a, (String, Type)>), + Anon(std::iter::Once<&'a Type>), +} + +impl<'a> Iterator for ResultsTypeIter<'a> { + type Item = &'a Type; + + fn next(&mut self) -> Option<&'a Type> { + match self { + ResultsTypeIter::Named(ps) => ps.next().map(|p| &p.1), + ResultsTypeIter::Anon(ty) => ty.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + ResultsTypeIter::Named(ps) => ps.size_hint(), + ResultsTypeIter::Anon(ty) => ty.size_hint(), + } + } +} + +impl<'a> ExactSizeIterator for ResultsTypeIter<'a> {} + +impl Results { + // For the common case of an empty results list. + pub fn empty() -> Results { + Results::Named(Vec::new()) + } + + pub fn len(&self) -> usize { + match self { + Results::Named(params) => params.len(), + Results::Anon(_) => 1, + } + } + + pub fn iter_types(&self) -> ResultsTypeIter { + match self { + Results::Named(ps) => ResultsTypeIter::Named(ps.iter()), + Results::Anon(ty) => ResultsTypeIter::Anon(std::iter::once(ty)), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub struct Function { pub docs: Docs, pub name: String, pub kind: FunctionKind, - pub params: Vec<(String, Type)>, - pub result: Type, + pub params: Params, + pub results: Results, } #[derive(Debug, Clone, PartialEq)] @@ -429,13 +482,19 @@ impl Interface { } TypeDefKind::Variant(v) => { for v in v.cases.iter() { - self.topo_visit_ty(&v.ty, list, visited); + if let Some(ty) = v.ty { + self.topo_visit_ty(&ty, list, visited); + } } } TypeDefKind::Option(ty) => self.topo_visit_ty(ty, list, visited), TypeDefKind::Result(r) => { - self.topo_visit_ty(&r.ok, list, visited); - self.topo_visit_ty(&r.err, list, visited); + if let Some(ok) = r.ok { + self.topo_visit_ty(&ok, list, visited); + } + if let Some(err) = r.err { + self.topo_visit_ty(&err, list, visited); + } } TypeDefKind::Union(u) => { for t in u.cases.iter() { @@ -443,11 +502,17 @@ impl Interface { } } TypeDefKind::Future(ty) => { - self.topo_visit_ty(ty, list, visited); + if let Some(ty) = ty { + self.topo_visit_ty(ty, list, visited); + } } TypeDefKind::Stream(s) => { - self.topo_visit_ty(&s.element, list, visited); - self.topo_visit_ty(&s.end, list, visited); + if let Some(element) = s.element { + self.topo_visit_ty(&element, list, visited); + } + if let Some(end) = s.end { + self.topo_visit_ty(&end, list, visited); + } } } list.push(id); @@ -461,8 +526,7 @@ impl Interface { pub fn all_bits_valid(&self, ty: &Type) -> bool { match ty { - Type::Unit - | Type::U8 + Type::U8 | Type::S8 | Type::U16 | Type::S16 diff --git a/crates/wit-parser/src/mangle.rs b/crates/wit-parser/src/mangle.rs index 8abe99ff1..d7f2b95ce 100644 --- a/crates/wit-parser/src/mangle.rs +++ b/crates/wit-parser/src/mangle.rs @@ -4,14 +4,15 @@ //! [here]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#canonical-module-type use crate::{ - Case, EnumCase, Field, Flag, Function, Interface, ResourceId, Stream, Type, TypeDefKind, - UnionCase, + Case, EnumCase, Field, Flag, Function, Interface, Params, ResourceId, Result_, Results, Stream, + Type, TypeDefKind, UnionCase, }; const CABI_VERSION: &str = "0.1"; enum PreSpace { False, + True, } impl Interface { @@ -27,12 +28,12 @@ impl Interface { format!( "{}: func{} -> {}", name, - self.mangle_funcvec(&func.params, PreSpace::False), - self.mangle_valtype(func.result) + self.mangle_param_types(&func.params, PreSpace::False), + self.mangle_result_types(&func.results, PreSpace::True) ) } - fn mangle_funcvec(&self, es: &[(String, Type)], _pre_space: PreSpace) -> String { + fn mangle_param_types(&self, es: &Params, _pre_space: PreSpace) -> String { format!( "({})", es.iter() @@ -42,9 +43,23 @@ impl Interface { ) } + fn mangle_result_types(&self, es: &Results, _pre_space: PreSpace) -> String { + match es { + Results::Named(es) => { + format!( + "({})", + es.iter() + .map(|e| format!("{}: {}", e.0, self.mangle_valtype(e.1))) + .collect::>() + .join(", ") + ) + } + Results::Anon(ty) => self.mangle_valtype(*ty), + } + } + fn mangle_valtype(&self, t: Type) -> String { match t { - Type::Unit => "unit".to_owned(), Type::Bool => "bool".to_owned(), Type::S8 => "s8".to_owned(), Type::U8 => "u8".to_owned(), @@ -73,7 +88,7 @@ impl Interface { TypeDefKind::Enum(e) => self.mangle_enumtype(&e.cases), TypeDefKind::Union(u) => self.mangle_uniontype(&u.cases), TypeDefKind::Option(t) => self.mangle_optiontype(*t), - TypeDefKind::Result(r) => self.mangle_resulttype(r.ok, r.err), + TypeDefKind::Result(r) => self.mangle_resulttype(r), TypeDefKind::Future(t) => self.mangle_futuretype(*t), TypeDefKind::Stream(s) => self.mangle_streamtype(s), TypeDefKind::Type(t) => self.mangle_valtype(*t), @@ -117,7 +132,14 @@ impl Interface { "variant {{ {} }}", cases .iter() - .map(|c| format!("{}{}", c.name, format!("({})", self.mangle_valtype(c.ty)),)) + .map(|c| format!( + "{}{}", + c.name, + match c.ty { + Some(ty) => format!("({})", self.mangle_valtype(ty)), + None => "".to_string(), + } + )) .collect::>() .join(", ") ) @@ -149,28 +171,81 @@ impl Interface { format!("option<{}>", self.mangle_valtype(t)) } - fn mangle_resulttype(&self, ok: Type, error: Type) -> String { - format!( - "result<{}, {}>", - self.mangle_valtype(ok), - self.mangle_valtype(error) - ) + fn mangle_resulttype(&self, result: &Result_) -> String { + match result { + Result_ { + ok: Some(ok), + err: Some(err), + } => { + format!( + "result<{}, {}>", + self.mangle_valtype(*ok), + self.mangle_valtype(*err) + ) + } + Result_ { + ok: None, + err: Some(err), + } => { + format!("result<_, {}>", self.mangle_valtype(*err)) + } + Result_ { + ok: Some(ok), + err: None, + } => { + format!("result<{}>", self.mangle_valtype(*ok)) + } + Result_ { + ok: None, + err: None, + } => { + format!("result",) + } + } } fn mangle_handletype(&self, id: ResourceId) -> String { format!("handle<{}>", self.resources[id].name) } - fn mangle_futuretype(&self, ty: Type) -> String { - format!("future<{}>", self.mangle_valtype(ty)) + fn mangle_futuretype(&self, ty: Option) -> String { + match ty { + Some(ty) => format!("future<{}>", self.mangle_valtype(ty)), + None => format!("future"), + } } fn mangle_streamtype(&self, stream: &Stream) -> String { - format!( - "stream<{}, {}>", - self.mangle_valtype(stream.element), - self.mangle_valtype(stream.end) - ) + match stream { + Stream { + element: Some(element), + end: Some(end), + } => { + format!( + "stream<{}, {}>", + self.mangle_valtype(*element), + self.mangle_valtype(*end) + ) + } + Stream { + element: None, + end: Some(end), + } => { + format!("stream<_, {}>", self.mangle_valtype(*end)) + } + Stream { + element: Some(element), + end: None, + } => { + format!("stream<{}>", self.mangle_valtype(*element)) + } + Stream { + element: None, + end: None, + } => { + format!("stream",) + } + } } } @@ -191,9 +266,9 @@ mod tests { name: "foo".to_owned(), kind: FunctionKind::Freestanding, params: Vec::new(), - result: Type::Unit + results: Results::empty(), }), - "foo: func() -> unit" + "foo: func() -> ()" ); assert_eq!( interface.mangle_funcname(&Function { @@ -201,7 +276,7 @@ mod tests { name: "foo".to_owned(), kind: FunctionKind::Freestanding, params: vec![("a".to_owned(), Type::S64)], - result: Type::S32 + results: Results::Anon(Type::S32) }), "foo: func(a: s64) -> s32" ); @@ -211,10 +286,23 @@ mod tests { name: "foo".to_owned(), kind: FunctionKind::Freestanding, params: vec![("a".to_owned(), Type::S64), ("b".to_owned(), Type::U64)], - result: Type::S32 + results: Results::Anon(Type::S32) }), "foo: func(a: s64, b: u64) -> s32" ); + assert_eq!( + interface.mangle_funcname(&Function { + docs: Docs::default(), + name: "foo".to_owned(), + kind: FunctionKind::Freestanding, + params: vec![("a".to_owned(), Type::S64), ("b".to_owned(), Type::U64)], + results: Results::Named(vec![ + ("x".to_owned(), Type::S32), + ("y".to_owned(), Type::U32) + ]) + }), + "foo: func(a: s64, b: u64) -> (x: s32, y: u32)" + ); } #[test] @@ -227,16 +315,15 @@ mod tests { name: "foo".to_owned(), kind: FunctionKind::Freestanding, params: Vec::new(), - result: Type::Unit + results: Results::empty(), }), - format!("cabi_start{{cabi={}}}: func() -> unit", CABI_VERSION) + format!("cabi_start{{cabi={}}}: func() -> ()", CABI_VERSION) ); } #[test] fn test_types() { let iface = Interface::default(); - assert_eq!(iface.mangle_valtype(Type::Unit), "unit"); assert_eq!(iface.mangle_valtype(Type::Bool), "bool"); assert_eq!(iface.mangle_valtype(Type::S8), "s8"); assert_eq!(iface.mangle_valtype(Type::U8), "u8"); @@ -363,7 +450,7 @@ mod tests { cases: vec![Case { name: "x".to_owned(), docs: Docs::default(), - ty: Type::Float32 + ty: Some(Type::Float32) }] })), "variant { x(float32) }" @@ -374,16 +461,21 @@ mod tests { Case { name: "x".to_owned(), docs: Docs::default(), - ty: Type::Float32 + ty: Some(Type::Float32) }, Case { name: "y".to_owned(), docs: Docs::default(), - ty: Type::Float64 + ty: Some(Type::Float64) + }, + Case { + name: "z".to_owned(), + docs: Docs::default(), + ty: None, } ] })), - "variant { x(float32), y(float64) }" + "variant { x(float32), y(float64), z }" ); } @@ -467,11 +559,32 @@ mod tests { let iface = Interface::default(); assert_eq!( iface.mangle_valtypedef(&TypeDefKind::Result(Result_ { - ok: Type::S32, - err: Type::U32 + ok: Some(Type::S32), + err: Some(Type::U32) })), "result" ); + assert_eq!( + iface.mangle_valtypedef(&TypeDefKind::Result(Result_ { + ok: None, + err: Some(Type::U32) + })), + "result<_, u32>" + ); + assert_eq!( + iface.mangle_valtypedef(&TypeDefKind::Result(Result_ { + ok: Some(Type::S32), + err: None + })), + "result" + ); + assert_eq!( + iface.mangle_valtypedef(&TypeDefKind::Result(Result_ { + ok: None, + err: None + })), + "result" + ); } #[test] @@ -490,9 +603,13 @@ mod tests { fn test_futuretype() { let iface = Interface::default(); assert_eq!( - iface.mangle_valtypedef(&TypeDefKind::Future(Type::S8)), + iface.mangle_valtypedef(&TypeDefKind::Future(Some(Type::S8))), "future" ); + assert_eq!( + iface.mangle_valtypedef(&TypeDefKind::Future(None)), + "future" + ); } #[test] @@ -500,10 +617,31 @@ mod tests { let iface = Interface::default(); assert_eq!( iface.mangle_valtypedef(&TypeDefKind::Stream(Stream { - element: Type::S8, - end: Type::U8 + element: Some(Type::S8), + end: Some(Type::U8) })), "stream" ); + assert_eq!( + iface.mangle_valtypedef(&TypeDefKind::Stream(Stream { + element: None, + end: Some(Type::U8) + })), + "stream<_, u8>" + ); + assert_eq!( + iface.mangle_valtypedef(&TypeDefKind::Stream(Stream { + element: Some(Type::S8), + end: None + })), + "stream" + ); + assert_eq!( + iface.mangle_valtypedef(&TypeDefKind::Stream(Stream { + element: None, + end: None + })), + "stream" + ); } } diff --git a/crates/wit-parser/src/sizealign.rs b/crates/wit-parser/src/sizealign.rs index 961bf90fa..aa6cc03b1 100644 --- a/crates/wit-parser/src/sizealign.rs +++ b/crates/wit-parser/src/sizealign.rs @@ -25,11 +25,11 @@ impl SizeAlign { FlagsRepr::U16 => (2, 2), FlagsRepr::U32(n) => (n * 4, 4), }, - TypeDefKind::Variant(v) => self.variant(v.tag(), v.cases.iter().map(|c| &c.ty)), + TypeDefKind::Variant(v) => self.variant(v.tag(), v.cases.iter().map(|c| c.ty.as_ref())), TypeDefKind::Enum(e) => self.variant(e.tag(), []), - TypeDefKind::Option(t) => self.variant(Int::U8, [&Type::Unit, t]), - TypeDefKind::Result(r) => self.variant(Int::U8, [&r.ok, &r.err]), - TypeDefKind::Union(u) => self.variant(u.tag(), u.cases.iter().map(|c| &c.ty)), + TypeDefKind::Option(t) => self.variant(Int::U8, [Some(t)]), + TypeDefKind::Result(r) => self.variant(Int::U8, [r.ok.as_ref(), r.err.as_ref()]), + TypeDefKind::Union(u) => self.variant(u.tag(), u.cases.iter().map(|c| Some(&c.ty))), // A future is represented as an index. TypeDefKind::Future(_) => (4, 4), // A stream is represented as an index. @@ -39,7 +39,6 @@ impl SizeAlign { pub fn size(&self, ty: &Type) -> usize { match ty { - Type::Unit => 0, Type::Bool | Type::U8 | Type::S8 => 1, Type::U16 | Type::S16 => 2, Type::U32 | Type::S32 | Type::Float32 | Type::Char | Type::Handle(_) => 4, @@ -50,7 +49,7 @@ impl SizeAlign { pub fn align(&self, ty: &Type) -> usize { match ty { - Type::Unit | Type::Bool | Type::U8 | Type::S8 => 1, + Type::Bool | Type::U8 | Type::S8 => 1, Type::U16 | Type::S16 => 2, Type::U32 | Type::S32 | Type::Float32 | Type::Char | Type::Handle(_) | Type::String => { 4 @@ -60,22 +59,31 @@ impl SizeAlign { } } - pub fn field_offsets<'a>(&self, types: impl IntoIterator) -> Vec { + pub fn field_offsets<'a>( + &self, + types: impl IntoIterator, + ) -> Vec<(usize, &'a Type)> { let mut cur = 0; types .into_iter() .map(|ty| { let ret = align_to(cur, self.align(ty)); cur = ret + self.size(ty); - ret + (ret, ty) }) .collect() } - pub fn payload_offset<'a>(&self, tag: Int, cases: impl IntoIterator) -> usize { + pub fn payload_offset<'a>( + &self, + tag: Int, + cases: impl IntoIterator>, + ) -> usize { let mut max_align = 1; for ty in cases { - max_align = max_align.max(self.align(ty)); + if let Some(ty) = ty { + max_align = max_align.max(self.align(ty)); + } } let tag_size = int_size_align(tag).0; align_to(tag_size, max_align) @@ -93,15 +101,25 @@ impl SizeAlign { (align_to(size, align), align) } - fn variant<'a>(&self, tag: Int, types: impl IntoIterator) -> (usize, usize) { + pub fn params<'a>(&self, types: impl IntoIterator) -> (usize, usize) { + self.record(types.into_iter()) + } + + fn variant<'a>( + &self, + tag: Int, + types: impl IntoIterator>, + ) -> (usize, usize) { let (discrim_size, discrim_align) = int_size_align(tag); let mut size = discrim_size; let mut align = discrim_align; for ty in types { - let case_size = self.size(ty); - let case_align = self.align(ty); - align = align.max(case_align); - size = size.max(align_to(discrim_size, case_align) + case_size); + if let Some(ty) = ty { + let case_size = self.size(ty); + let case_align = self.align(ty); + align = align.max(case_align); + size = size.max(align_to(discrim_size, case_align) + case_size); + } } (size, align) } diff --git a/crates/wit-parser/tests/all.rs b/crates/wit-parser/tests/all.rs index f17cfebb5..c46e91d6f 100644 --- a/crates/wit-parser/tests/all.rs +++ b/crates/wit-parser/tests/all.rs @@ -202,24 +202,42 @@ fn to_json(i: &Interface) -> String { #[serde(rename_all = "kebab-case")] enum Type { Primitive(String), - Record { fields: Vec<(String, String)> }, - Flags { flags: Vec }, - Enum { cases: Vec }, - Variant { cases: Vec<(String, String)> }, - Tuple { types: Vec }, + Record { + fields: Vec<(String, String)>, + }, + Flags { + flags: Vec, + }, + Enum { + cases: Vec, + }, + Variant { + cases: Vec<(String, Option)>, + }, + Tuple { + types: Vec, + }, Option(String), - Result { ok: String, err: String }, - Future(String), - Stream { element: String, end: String }, + Result { + ok: Option, + err: Option, + }, + Future(Option), + Stream { + element: Option, + end: Option, + }, List(String), - Union { cases: Vec }, + Union { + cases: Vec, + }, } #[derive(Serialize)] struct Function { name: String, params: Vec, - result: String, + results: Vec, } #[derive(Serialize)] @@ -254,7 +272,11 @@ fn to_json(i: &Interface) -> String { .map(|f| Function { name: f.name.clone(), params: f.params.iter().map(|(_, ty)| translate_type(ty)).collect(), - result: translate_type(&f.result), + results: f + .results + .iter_types() + .map(|ty| translate_type(ty)) + .collect(), }) .collect::>(); let globals = i @@ -297,18 +319,18 @@ fn to_json(i: &Interface) -> String { cases: v .cases .iter() - .map(|f| (f.name.clone(), translate_type(&f.ty))) + .map(|f| (f.name.clone(), translate_optional_type(f.ty.as_ref()))) .collect(), }, TypeDefKind::Option(t) => Type::Option(translate_type(t)), TypeDefKind::Result(r) => Type::Result { - ok: translate_type(&r.ok), - err: translate_type(&r.err), + ok: translate_optional_type(r.ok.as_ref()), + err: translate_optional_type(r.err.as_ref()), }, - TypeDefKind::Future(t) => Type::Future(translate_type(t)), + TypeDefKind::Future(t) => Type::Future(translate_optional_type(t.as_ref())), TypeDefKind::Stream(s) => Type::Stream { - element: translate_type(&s.element), - end: translate_type(&s.end), + element: translate_optional_type(s.element.as_ref()), + end: translate_optional_type(s.end.as_ref()), }, TypeDefKind::List(ty) => Type::List(translate_type(ty)), TypeDefKind::Union(u) => Type::Union { @@ -320,7 +342,6 @@ fn to_json(i: &Interface) -> String { fn translate_type(ty: &wit_parser::Type) -> String { use wit_parser::Type; match ty { - Type::Unit => format!("unit"), Type::Bool => format!("bool"), Type::U8 => format!("u8"), Type::U16 => format!("u16"), @@ -338,4 +359,8 @@ fn to_json(i: &Interface) -> String { Type::Id(id) => format!("type-{}", id.index()), } } + + fn translate_optional_type(ty: Option<&wit_parser::Type>) -> Option { + ty.map(translate_type) + } } diff --git a/crates/wit-parser/tests/ui/embedded.wit.md.result b/crates/wit-parser/tests/ui/embedded.wit.md.result index 661095c19..0e6b440ff 100644 --- a/crates/wit-parser/tests/ui/embedded.wit.md.result +++ b/crates/wit-parser/tests/ui/embedded.wit.md.result @@ -3,17 +3,17 @@ { "name": "x", "params": [], - "result": "unit" + "results": [] }, { "name": "y", "params": [], - "result": "unit" + "results": [] }, { "name": "z", "params": [], - "result": "unit" + "results": [] } ] } \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/functions.wit b/crates/wit-parser/tests/ui/functions.wit index 8ee64ba70..94804a6b3 100644 --- a/crates/wit-parser/tests/ui/functions.wit +++ b/crates/wit-parser/tests/ui/functions.wit @@ -5,3 +5,6 @@ f4: func() -> u32 f6: func() -> tuple f7: func(a: float32, b: float32) -> tuple f8: func(a: option) -> result +f9: func() -> (u: u32, f: float32) +f10: func() -> (u: u32) +f11: func() -> () diff --git a/crates/wit-parser/tests/ui/functions.wit.result b/crates/wit-parser/tests/ui/functions.wit.result index b4656d976..3e451c9f4 100644 --- a/crates/wit-parser/tests/ui/functions.wit.result +++ b/crates/wit-parser/tests/ui/functions.wit.result @@ -25,31 +25,35 @@ { "name": "f1", "params": [], - "result": "unit" + "results": [] }, { "name": "f2", "params": [ "u32" ], - "result": "unit" + "results": [] }, { "name": "f3", "params": [ "u32" ], - "result": "unit" + "results": [] }, { "name": "f4", "params": [], - "result": "u32" + "results": [ + "u32" + ] }, { "name": "f6", "params": [], - "result": "type-0" + "results": [ + "type-0" + ] }, { "name": "f7", @@ -57,14 +61,38 @@ "float32", "float32" ], - "result": "type-0" + "results": [ + "type-0" + ] }, { "name": "f8", "params": [ "type-1" ], - "result": "type-2" + "results": [ + "type-2" + ] + }, + { + "name": "f9", + "params": [], + "results": [ + "u32", + "float32" + ] + }, + { + "name": "f10", + "params": [], + "results": [ + "u32" + ] + }, + { + "name": "f11", + "params": [], + "results": [] } ] } \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/resource.wit.result b/crates/wit-parser/tests/ui/resource.wit.result index b09cb8486..77ca4b912 100644 --- a/crates/wit-parser/tests/ui/resource.wit.result +++ b/crates/wit-parser/tests/ui/resource.wit.result @@ -75,28 +75,28 @@ "params": [ "handle-4" ], - "result": "unit" + "results": [] }, { "name": "f::x", "params": [ "handle-5" ], - "result": "unit" + "results": [] }, { "name": "f::y", "params": [ "handle-5" ], - "result": "unit" + "results": [] }, { "name": "i::z", "params": [ "handle-8" ], - "result": "unit" + "results": [] } ] } \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/type-then-eof.wit.result b/crates/wit-parser/tests/ui/type-then-eof.wit.result index ae651911a..625b858e2 100644 --- a/crates/wit-parser/tests/ui/type-then-eof.wit.result +++ b/crates/wit-parser/tests/ui/type-then-eof.wit.result @@ -3,7 +3,9 @@ { "name": "foo", "params": [], - "result": "string" + "results": [ + "string" + ] } ] } \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/types.wit b/crates/wit-parser/tests/ui/types.wit index 5d2cafda9..dac40a796 100644 --- a/crates/wit-parser/tests/ui/types.wit +++ b/crates/wit-parser/tests/ui/types.wit @@ -15,9 +15,9 @@ type t12 = list type t13 = string type t14 = option type t15 = result -type t16 = result -type t17 = result -type t18 = result +type t16 = result<_, u32> +type t17 = result +type t18 = result type t19 = handle x record t20 {} record t21 { a: u32 } @@ -49,11 +49,11 @@ type t45 = list>> type t46 = t44 type t47 = %t44 type t48 = stream -type t49 = stream -type t50 = stream -type t51 = stream +type t49 = stream<_, u32> +type t50 = stream +type t51 = stream type t52 = future -type t53 = future +type t53 = future // type order doesn't matter type foo = bar diff --git a/crates/wit-parser/tests/ui/types.wit.result b/crates/wit-parser/tests/ui/types.wit.result index 094418942..e9ae53339 100644 --- a/crates/wit-parser/tests/ui/types.wit.result +++ b/crates/wit-parser/tests/ui/types.wit.result @@ -91,7 +91,7 @@ "idx": 16, "name": "t16", "result": { - "ok": "unit", + "ok": null, "err": "u32" } }, @@ -100,15 +100,15 @@ "name": "t17", "result": { "ok": "u32", - "err": "unit" + "err": null } }, { "idx": 18, "name": "t18", "result": { - "ok": "unit", - "err": "unit" + "ok": null, + "err": null } }, { @@ -269,7 +269,7 @@ "cases": [ [ "a", - "unit" + null ] ] } @@ -281,11 +281,11 @@ "cases": [ [ "a", - "unit" + null ], [ "b", - "unit" + null ] ] } @@ -297,11 +297,11 @@ "cases": [ [ "a", - "unit" + null ], [ "b", - "unit" + null ] ] } @@ -313,7 +313,7 @@ "cases": [ [ "a", - "unit" + null ], [ "b", @@ -329,7 +329,7 @@ "cases": [ [ "a", - "unit" + null ], [ "b", @@ -427,7 +427,7 @@ "idx": 50, "name": "t49", "stream": { - "element": "unit", + "element": null, "end": "u32" } }, @@ -436,15 +436,15 @@ "name": "t50", "stream": { "element": "u32", - "end": "unit" + "end": null } }, { "idx": 52, "name": "t51", "stream": { - "element": "unit", - "end": "unit" + "element": null, + "end": null } }, { @@ -455,7 +455,7 @@ { "idx": 54, "name": "t53", - "future": "unit" + "future": null }, { "idx": 55, diff --git a/crates/wit-parser/tests/ui/wasi-clock.wit.result b/crates/wit-parser/tests/ui/wasi-clock.wit.result index 8a91d9cf4..b8ab2a996 100644 --- a/crates/wit-parser/tests/ui/wasi-clock.wit.result +++ b/crates/wit-parser/tests/ui/wasi-clock.wit.result @@ -117,7 +117,9 @@ "params": [ "type-0" ], - "result": "type-3" + "results": [ + "type-3" + ] }, { "name": "time-get", @@ -125,7 +127,9 @@ "type-0", "type-1" ], - "result": "type-3" + "results": [ + "type-3" + ] } ] } \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/wasi-http.wit.result b/crates/wit-parser/tests/ui/wasi-http.wit.result index 0ce263314..52cf7a8cc 100644 --- a/crates/wit-parser/tests/ui/wasi-http.wit.result +++ b/crates/wit-parser/tests/ui/wasi-http.wit.result @@ -78,54 +78,70 @@ "params": [ "handle-0" ], - "result": "handle-1" + "results": [ + "handle-1" + ] }, { "name": "request::request", "params": [], - "result": "handle-0" + "results": [ + "handle-0" + ] }, { "name": "request::method", "params": [ "handle-0" ], - "result": "string" + "results": [ + "string" + ] }, { "name": "request::headers", "params": [ "handle-0" ], - "result": "handle-2" + "results": [ + "handle-2" + ] }, { "name": "request::body", "params": [ "handle-0" ], - "result": "handle-3" + "results": [ + "handle-3" + ] }, { "name": "response::status", "params": [ "handle-1" ], - "result": "u16" + "results": [ + "u16" + ] }, { "name": "response::headers", "params": [ "handle-1" ], - "result": "handle-2" + "results": [ + "handle-2" + ] }, { "name": "response::body", "params": [ "handle-1" ], - "result": "handle-3" + "results": [ + "handle-3" + ] }, { "name": "headers::get", @@ -133,7 +149,9 @@ "handle-2", "string" ], - "result": "type-6" + "results": [ + "type-6" + ] }, { "name": "body::read", @@ -141,7 +159,9 @@ "handle-3", "type-7" ], - "result": "type-8" + "results": [ + "type-8" + ] }, { "name": "body::write", @@ -149,12 +169,16 @@ "handle-3", "type-7" ], - "result": "type-8" + "results": [ + "type-8" + ] }, { "name": "maybe-number", "params": [], - "result": "type-8" + "results": [ + "type-8" + ] } ], "globals": [ diff --git a/tests/codegen/multi-return.wit b/tests/codegen/multi-return.wit new file mode 100644 index 000000000..880e1c5fc --- /dev/null +++ b/tests/codegen/multi-return.wit @@ -0,0 +1,5 @@ +a: func() +b: func() -> () +c: func() -> u32 +d: func() -> (a: u32) +e: func() -> (a: u32, b: float32) diff --git a/tests/codegen/variants.wit b/tests/codegen/variants.wit index 2bc9e904c..03909fcbc 100644 --- a/tests/codegen/variants.wit +++ b/tests/codegen/variants.wit @@ -97,17 +97,17 @@ casts: func( > result-arg: func( - a: result, - b: result, - c: result, + a: result, + b: result<_, e1>, + c: result, d: result, tuple<>>, e: result, f: result>, ) result-result: func() -> tuple< - result, - result, - result, + result, + result<_, e1>, + result, result, tuple<>>, result, result>, @@ -119,7 +119,7 @@ enum my-errno { } return-result-sugar: func() -> result -return-result-sugar2: func() -> result +return-result-sugar2: func() -> result<_, my-errno> return-result-sugar3: func() -> result return-result-sugar4: func() -> result, my-errno> return-option-sugar: func() -> option @@ -133,3 +133,6 @@ record is-clone { is-clone-arg: func(a: is-clone) is-clone-return: func() -> is-clone + +return-named-option: func() -> (a: option) +return-named-result: func() -> (a: result) diff --git a/tests/runtime/flavorful/exports.wit b/tests/runtime/flavorful/exports.wit index 9804f6604..be79aace7 100644 --- a/tests/runtime/flavorful/exports.wit +++ b/tests/runtime/flavorful/exports.wit @@ -12,7 +12,7 @@ list-in-record3: func(a: list-in-record3) -> list-in-record3 list-in-record4: func(a: list-in-alias) -> list-in-alias type list-in-variant1-v1 = option -type list-in-variant1-v2 = result +type list-in-variant1-v2 = result<_, string> union list-in-variant1-v3 { string, float32 } list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2, c: list-in-variant1-v3) @@ -23,9 +23,10 @@ type list-in-variant3 = option list-in-variant3: func(a: list-in-variant3) -> list-in-variant3 enum my-errno { success, a, b } -errno-result: func() -> result +errno-result: func() -> result<_, my-errno> type list-typedef = string type list-typedef2 = list type list-typedef3 = list -list-typedefs: func(a: list-typedef, c: list-typedef3) -> tuple +list-typedefs: func(a: list-typedef, c: list-typedef3) + -> (a: list-typedef2, b: list-typedef3) diff --git a/tests/runtime/flavorful/imports.wit b/tests/runtime/flavorful/imports.wit index d5838f840..242d95433 100644 --- a/tests/runtime/flavorful/imports.wit +++ b/tests/runtime/flavorful/imports.wit @@ -10,7 +10,7 @@ list-in-record3: func(a: list-in-record3) -> list-in-record3 list-in-record4: func(a: list-in-alias) -> list-in-alias type list-in-variant1-v1 = option -type list-in-variant1-v2 = result +type list-in-variant1-v2 = result<_, string> union list-in-variant1-v3 { string, float32 } list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2, c: list-in-variant1-v3) @@ -21,11 +21,12 @@ type list-in-variant3 = option list-in-variant3: func(a: list-in-variant3) -> list-in-variant3 enum my-errno { success, a, b } -errno-result: func() -> result +errno-result: func() -> result<_, my-errno> type list-typedef = string type list-typedef2 = list type list-typedef3 = list -list-typedefs: func(a: list-typedef, c: list-typedef3) -> tuple +list-typedefs: func(a: list-typedef, c: list-typedef3) -> (a: list-typedef2, b: list-typedef3) -list-of-variants: func(a: list, b: list>, c: list) -> tuple, list>, list> +list-of-variants: func(a: list, b: list, c: list) + -> (a: list, b: list, c: list) diff --git a/tests/runtime/flavorful/wasm.c b/tests/runtime/flavorful/wasm.c index ba72be5e9..360809bc3 100644 --- a/tests/runtime/flavorful/wasm.c +++ b/tests/runtime/flavorful/wasm.c @@ -90,8 +90,8 @@ void exports_test_imports() { a.ptr = a_val; a.len = 2; - imports_list_result_unit_unit_t b; - imports_result_unit_unit_t b_val[2]; + imports_list_result_void_void_t b; + imports_result_void_void_t b_val[2]; b_val[0].is_err = false; b_val[1].is_err = true; b.ptr = b_val; @@ -105,7 +105,7 @@ void exports_test_imports() { c.len = 2; imports_list_bool_t d; - imports_list_result_unit_unit_t e; + imports_list_result_void_void_t e; imports_list_my_errno_t f; imports_list_of_variants(&a, &b, &c, &d, &e, &f); @@ -122,7 +122,7 @@ void exports_test_imports() { assert(f.ptr[1] == IMPORTS_MY_ERRNO_B); imports_list_bool_free(&d); - imports_list_result_unit_unit_free(&e); + imports_list_result_void_void_free(&e); imports_list_my_errno_free(&f); } } diff --git a/tests/runtime/handles/exports.wit b/tests/runtime/handles/exports.wit index 87f3e7a90..cc6aab0f7 100644 --- a/tests/runtime/handles/exports.wit +++ b/tests/runtime/handles/exports.wit @@ -8,7 +8,7 @@ wasm-state-get-val: func(a: wasm-state) -> u32 wasm-state2-create: func() -> wasm-state2 wasm-state2-saw-close: func() -> bool -two-wasm-states: func(a: wasm-state, b: wasm-state2) -> tuple +two-wasm-states: func(a: wasm-state, b: wasm-state2) -> (a: wasm-state, b: wasm-state2) record wasm-state-param-record { a: wasm-state2 } wasm-state2-param-record: func(a: wasm-state-param-record) diff --git a/tests/runtime/handles/imports.wit b/tests/runtime/handles/imports.wit index 162de15b0..f83f46372 100644 --- a/tests/runtime/handles/imports.wit +++ b/tests/runtime/handles/imports.wit @@ -6,7 +6,7 @@ host-state-get: func(a: host-state) -> u32 host-state2-create: func() -> host-state2 host-state2-saw-close: func() -> bool -two-host-states: func(a: host-state, b: host-state2) -> tuple +two-host-states: func(a: host-state, b: host-state2) -> (a: host-state, b: host-state2) record host-state-param-record { a: host-state2 } host-state2-param-record: func(a: host-state-param-record) diff --git a/tests/runtime/handles/wasm.c b/tests/runtime/handles/wasm.c index 850a48108..ef95592f8 100644 --- a/tests/runtime/handles/wasm.c +++ b/tests/runtime/handles/wasm.c @@ -83,9 +83,9 @@ void exports_test_imports() { imports_host_state2_free(&a.a); } { - imports_host_state2_t a; + imports_host_state_result_tuple_t a; imports_host_state2_result_tuple(&a); - imports_host_state2_free(&a); + imports_host_state2_free(&a.f0); } { imports_host_state2_t a; @@ -184,8 +184,8 @@ void exports_wasm_state2_result_record(exports_wasm_state_result_record_t *ret0) ret0->a = exports_wasm_state2_new((void*) 222); } -void exports_wasm_state2_result_tuple(exports_wasm_state2_t *ret0) { - *ret0 = exports_wasm_state2_new((void*) 333); +void exports_wasm_state2_result_tuple(exports_wasm_state_result_tuple_t *ret0) { + ret0->f0 = exports_wasm_state2_new((void*) 333); } bool exports_wasm_state2_result_option(exports_wasm_state2_t *ret0) { diff --git a/tests/runtime/lists/imports.wit b/tests/runtime/lists/imports.wit index 5c13f0e0a..57fc2a52d 100644 --- a/tests/runtime/lists/imports.wit +++ b/tests/runtime/lists/imports.wit @@ -29,11 +29,12 @@ list-result: func() -> list list-result2: func() -> string list-result3: func() -> list -list-minmax8: func(a: list, b: list) -> tuple, list> -list-minmax16: func(a: list, b: list) -> tuple, list> -list-minmax32: func(a: list, b: list) -> tuple, list> -list-minmax64: func(a: list, b: list) -> tuple, list> -list-minmax-float: func(a: list, b: list) -> tuple, list> +list-minmax8: func(a: list, b: list) -> (a: list, b: list) +list-minmax16: func(a: list, b: list) -> (a: list, b: list) +list-minmax32: func(a: list, b: list) -> (a: list, b: list) +list-minmax64: func(a: list, b: list) -> (a: list, b: list) +list-minmax-float: func(a: list, b: list) + -> (a: list, b: list) list-roundtrip: func(a: list) -> list diff --git a/tests/runtime/records/exports.wit b/tests/runtime/records/exports.wit index 45dcdbf15..336743d62 100644 --- a/tests/runtime/records/exports.wit +++ b/tests/runtime/records/exports.wit @@ -1,6 +1,6 @@ test-imports: func() -multiple-results: func() -> tuple +multiple-results: func() -> (a: u8, b: u16) swap-tuple: func(a: tuple) -> tuple @@ -37,7 +37,8 @@ flags f64 { b56, b57, b58, b59, b60, b61, b62, b63, } -roundtrip-flags3: func(a: f8, b: f16, c: f32, d: f64) -> tuple +roundtrip-flags3: func(a: f8, b: f16, c: f32, d: f64) -> + (f8: f8, f16: f16, f32: f32, f64: f64) record r1 { a: u8, b: f1 } roundtrip-record1: func(a: r1) -> r1 diff --git a/tests/runtime/records/imports.wit b/tests/runtime/records/imports.wit index 304fa31ad..fb6fa72f4 100644 --- a/tests/runtime/records/imports.wit +++ b/tests/runtime/records/imports.wit @@ -1,4 +1,4 @@ -multiple-results: func() -> tuple +multiple-results: func() -> (a: u8, b: u16) swap-tuple: func(a: tuple) -> tuple @@ -35,7 +35,8 @@ flags flag64 { b56, b57, b58, b59, b60, b61, b62, b63, } -roundtrip-flags3: func(a: flag8, b: flag16, c: flag32, d: flag64) -> tuple +roundtrip-flags3: func(a: flag8, b: flag16, c: flag32, d: flag64) -> + (f8: flag8, f16: flag16, f32: flag32, f64: flag64) record r1 { a: u8, b: f1 } roundtrip-record1: func(a: r1) -> r1 diff --git a/tests/runtime/records/wasm.c b/tests/runtime/records/wasm.c index 0b6245244..8f5e28961 100644 --- a/tests/runtime/records/wasm.c +++ b/tests/runtime/records/wasm.c @@ -12,13 +12,12 @@ void exports_test_imports() { } imports_tuple2_u8_u32_t input; + imports_tuple2_u32_u8_t output; input.f0 = 1; input.f1 = 2; - uint32_t a; - uint8_t b; - imports_swap_tuple(&input, &a, &b); - assert(a == 2); - assert(b == 1); + imports_swap_tuple(&input, &output); + assert(output.f0 == 2); + assert(output.f1 == 1); assert(imports_roundtrip_flags1(IMPORTS_F1_A) == IMPORTS_F1_A); assert(imports_roundtrip_flags1(0) == 0); @@ -60,13 +59,12 @@ void exports_test_imports() { } imports_tuple0_t t0; - imports_tuple0(&t0); + imports_tuple0(&t0, &t0); - imports_tuple1_u8_t t1; + imports_tuple1_u8_t t1, t2; t1.f0 = 1; - uint8_t ret; - imports_tuple1(&t1, &ret); - assert(ret == 1); + imports_tuple1(&t1, &t2); + assert(t2.f0 == 1); } void exports_multiple_results(uint8_t *ret0, uint16_t *ret1) { @@ -74,9 +72,9 @@ void exports_multiple_results(uint8_t *ret0, uint16_t *ret1) { *ret1 = 200; } -void exports_swap_tuple(exports_tuple2_u8_u32_t *a, uint32_t *ret0, uint8_t *ret1) { - *ret0 = a->f1; - *ret1 = a->f0; +void exports_swap_tuple(exports_tuple2_u8_u32_t *a, exports_tuple2_u32_u8_t *b) { + b->f0 = a->f1; + b->f1 = a->f0; } exports_f1_t exports_roundtrip_flags1(exports_f1_t a) { @@ -98,9 +96,9 @@ void exports_roundtrip_record1(exports_r1_t *a, exports_r1_t *ret0) { *ret0 = *a; } -void exports_tuple0(exports_tuple0_t *a) { +void exports_tuple0(exports_tuple0_t *a, exports_tuple0_t *b) { } -void exports_tuple1(exports_tuple1_u8_t *a, uint8_t *ret0) { - *ret0 = a->f0; +void exports_tuple1(exports_tuple1_u8_t *a, exports_tuple1_u8_t *b) { + b->f0 = a->f0; } diff --git a/tests/runtime/variants/exports.wit b/tests/runtime/variants/exports.wit index 743be17d5..ef774491b 100644 --- a/tests/runtime/variants/exports.wit +++ b/tests/runtime/variants/exports.wit @@ -26,5 +26,5 @@ variant-zeros: func(a: zeros) -> zeros type option-typedef = option type bool-typedef = bool -type result-typedef = result +type result-typedef = result variant-typedefs: func(a: option-typedef, b: bool-typedef, c: result-typedef) diff --git a/tests/runtime/variants/imports.wit b/tests/runtime/variants/imports.wit index f0c442a23..b99a64441 100644 --- a/tests/runtime/variants/imports.wit +++ b/tests/runtime/variants/imports.wit @@ -24,8 +24,8 @@ variant-zeros: func(a: zeros) -> zeros type option-typedef = option type bool-typedef = bool -type result-typedef = result +type result-typedef = result variant-typedefs: func(a: option-typedef, b: bool-typedef, c: result-typedef) enum my-errno { success, a, b } -variant-enums: func(a: bool, b: result, c: my-errno) -> tuple, my-errno> +variant-enums: func(a: bool, b: result, c: my-errno) -> tuple diff --git a/tests/runtime/variants/wasm.c b/tests/runtime/variants/wasm.c index 340201860..533b6e7bb 100644 --- a/tests/runtime/variants/wasm.c +++ b/tests/runtime/variants/wasm.c @@ -47,13 +47,7 @@ void exports_test_imports() { assert(imports_invert_bool(false) == true); { - imports_casts_t c; - imports_c1_t r1; - imports_c2_t r2; - imports_c3_t r3; - imports_c4_t r4; - imports_c5_t r5; - imports_c6_t r6; + imports_casts_t c, ret; c.f0.tag = IMPORTS_C1_A; c.f0.val.a = 1; c.f1.tag = IMPORTS_C2_A; @@ -66,23 +60,17 @@ void exports_test_imports() { c.f4.val.a = 5; c.f5.tag = IMPORTS_C6_A; c.f5.val.a = 6; - imports_variant_casts(&c, &r1, &r2, &r3, &r4, &r5, &r6); - assert(r1.tag == IMPORTS_C1_A && r1.val.a == 1); - assert(r2.tag == IMPORTS_C2_A && r2.val.a == 2); - assert(r3.tag == IMPORTS_C3_A && r3.val.a == 3); - assert(r4.tag == IMPORTS_C4_A && r4.val.a == 4); - assert(r5.tag == IMPORTS_C5_A && r5.val.a == 5); - assert(r6.tag == IMPORTS_C6_A && r6.val.a == 6); + imports_variant_casts(&c, &ret); + assert(ret.f0.tag == IMPORTS_C1_A && ret.f0.val.a == 1); + assert(ret.f1.tag == IMPORTS_C2_A && ret.f1.val.a == 2); + assert(ret.f2.tag == IMPORTS_C3_A && ret.f2.val.a == 3); + assert(ret.f3.tag == IMPORTS_C4_A && ret.f3.val.a == 4); + assert(ret.f4.tag == IMPORTS_C5_A && ret.f4.val.a == 5); + assert(ret.f5.tag == IMPORTS_C6_A && ret.f5.val.a == 6); } { - imports_casts_t c; - imports_c1_t r1; - imports_c2_t r2; - imports_c3_t r3; - imports_c4_t r4; - imports_c5_t r5; - imports_c6_t r6; + imports_casts_t c, ret; c.f0.tag = IMPORTS_C1_B; c.f0.val.b = 1; c.f1.tag = IMPORTS_C2_B; @@ -95,21 +83,17 @@ void exports_test_imports() { c.f4.val.b = 5; c.f5.tag = IMPORTS_C6_B; c.f5.val.b = 6; - imports_variant_casts(&c, &r1, &r2, &r3, &r4, &r5, &r6); - assert(r1.tag == IMPORTS_C1_B && r1.val.b == 1); - assert(r2.tag == IMPORTS_C2_B && r2.val.b == 2); - assert(r3.tag == IMPORTS_C3_B && r3.val.b == 3); - assert(r4.tag == IMPORTS_C4_B && r4.val.b == 4); - assert(r5.tag == IMPORTS_C5_B && r5.val.b == 5); - assert(r6.tag == IMPORTS_C6_B && r6.val.b == 6); + imports_variant_casts(&c, &ret); + assert(ret.f0.tag == IMPORTS_C1_B && ret.f0.val.b == 1); + assert(ret.f1.tag == IMPORTS_C2_B && ret.f1.val.b == 2); + assert(ret.f2.tag == IMPORTS_C3_B && ret.f2.val.b == 3); + assert(ret.f3.tag == IMPORTS_C4_B && ret.f3.val.b == 4); + assert(ret.f4.tag == IMPORTS_C5_B && ret.f4.val.b == 5); + assert(ret.f5.tag == IMPORTS_C6_B && ret.f5.val.b == 6); } { - imports_zeros_t c; - imports_z1_t r1; - imports_z2_t r2; - imports_z3_t r3; - imports_z4_t r4; + imports_zeros_t c, ret; c.f0.tag = IMPORTS_Z1_A; c.f0.val.a = 1; c.f1.tag = IMPORTS_Z2_A; @@ -118,28 +102,24 @@ void exports_test_imports() { c.f2.val.a = 3; c.f3.tag = IMPORTS_Z4_A; c.f3.val.a = 4; - imports_variant_zeros(&c, &r1, &r2, &r3, &r4); - assert(r1.tag == IMPORTS_Z1_A && r1.val.a == 1); - assert(r2.tag == IMPORTS_Z2_A && r2.val.a == 2); - assert(r3.tag == IMPORTS_Z3_A && r3.val.a == 3); - assert(r4.tag == IMPORTS_Z4_A && r4.val.a == 4); + imports_variant_zeros(&c, &ret); + assert(ret.f0.tag == IMPORTS_Z1_A && ret.f0.val.a == 1); + assert(ret.f1.tag == IMPORTS_Z2_A && ret.f1.val.a == 2); + assert(ret.f2.tag == IMPORTS_Z3_A && ret.f2.val.a == 3); + assert(ret.f3.tag == IMPORTS_Z4_A && ret.f3.val.a == 4); } { - imports_zeros_t c; - imports_z1_t r1; - imports_z2_t r2; - imports_z3_t r3; - imports_z4_t r4; + imports_zeros_t c, ret; c.f0.tag = IMPORTS_Z1_B; c.f1.tag = IMPORTS_Z2_B; c.f2.tag = IMPORTS_Z3_B; c.f3.tag = IMPORTS_Z4_B; - imports_variant_zeros(&c, &r1, &r2, &r3, &r4); - assert(r1.tag == IMPORTS_Z1_B); - assert(r2.tag == IMPORTS_Z2_B); - assert(r3.tag == IMPORTS_Z3_B); - assert(r4.tag == IMPORTS_Z4_B); + imports_variant_zeros(&c, &ret); + assert(ret.f0.tag == IMPORTS_Z1_B); + assert(ret.f1.tag == IMPORTS_Z2_B); + assert(ret.f2.tag == IMPORTS_Z3_B); + assert(ret.f3.tag == IMPORTS_Z4_B); } { @@ -152,14 +132,13 @@ void exports_test_imports() { } { - bool a; - imports_result_unit_unit_t b; - imports_my_errno_t c; + imports_tuple3_bool_result_void_void_my_errno_t ret; + imports_result_void_void_t b; b.is_err = false; - imports_variant_enums(true, &b, IMPORTS_MY_ERRNO_SUCCESS, &a, &b, &c); - assert(a == false); - assert(b.is_err); - assert(c == IMPORTS_MY_ERRNO_A); + imports_variant_enums(true, &b, IMPORTS_MY_ERRNO_SUCCESS, &ret); + assert(ret.f0 == false); + assert(ret.f1.is_err); + assert(ret.f2 == IMPORTS_MY_ERRNO_A); } } @@ -187,20 +166,12 @@ bool exports_invert_bool(bool a) { return !a; } -void exports_variant_casts(exports_casts_t *a, exports_c1_t *ret0, exports_c2_t *ret1, exports_c3_t *ret2, exports_c4_t *ret3, exports_c5_t *ret4, exports_c6_t *ret5) { - *ret0 = a->f0; - *ret1 = a->f1; - *ret2 = a->f2; - *ret3 = a->f3; - *ret4 = a->f4; - *ret5 = a->f5; +void exports_variant_casts(exports_casts_t *a, exports_casts_t *ret) { + *ret = *a; } -void exports_variant_zeros(exports_zeros_t *a, exports_z1_t *ret0, exports_z2_t *ret1, exports_z3_t *ret2, exports_z4_t *ret3) { - *ret0 = a->f0; - *ret1 = a->f1; - *ret2 = a->f2; - *ret3 = a->f3; +void exports_variant_zeros(exports_zeros_t *a, exports_zeros_t *b) { + *b = *a; } void exports_variant_typedefs(exports_option_typedef_t *a, exports_bool_typedef_t b, exports_result_typedef_t *c) {