diff --git a/Cargo.lock b/Cargo.lock index 8af525b84669..6882a7a8ee4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3261,8 +3261,7 @@ checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "wasm-encoder" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8905fd25fdadeb0e7e8bf43a9f46f9f972d6291ad0c7a32573b88dd13a6cfa6b" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=component-wast#458426690fdc1a598e9f033fa3c290de3a30cd76" dependencies = [ "leb128", ] @@ -3270,8 +3269,7 @@ dependencies = [ [[package]] name = "wasm-mutate" version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e6de18ed96f27d3942041e5ae02177aff18e4425196a3d4b1f14145d027f71" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=component-wast#458426690fdc1a598e9f033fa3c290de3a30cd76" dependencies = [ "egg", "log", @@ -3284,8 +3282,7 @@ dependencies = [ [[package]] name = "wasm-smith" version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54f72dd89c036847831ef4d3b8f7fd8618d87509422728f12b0937f96d6dd04" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=component-wast#458426690fdc1a598e9f033fa3c290de3a30cd76" dependencies = [ "arbitrary", "flagset", @@ -3331,8 +3328,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.88.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8cf7dd82407fe68161bedcd57fde15596f32ebf6e9b3bdbf3ae1da20e38e5e" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=component-wast#458426690fdc1a598e9f033fa3c290de3a30cd76" dependencies = [ "indexmap", ] @@ -3340,8 +3336,7 @@ dependencies = [ [[package]] name = "wasmprinter" version = "0.2.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f2786f19a25211ddfa331e28b7579a6d6880f5f4b18d21253cd90274aa4c21" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=component-wast#458426690fdc1a598e9f033fa3c290de3a30cd76" dependencies = [ "anyhow", "wasmparser", @@ -3760,8 +3755,7 @@ dependencies = [ [[package]] name = "wast" version = "45.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186c474c4f9bb92756b566d592a16591b4526b1a4841171caa3f31d7fe330d96" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=component-wast#458426690fdc1a598e9f033fa3c290de3a30cd76" dependencies = [ "leb128", "memchr", @@ -3772,8 +3766,7 @@ dependencies = [ [[package]] name = "wat" version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d4bc4724b4f02a482c8cab053dac5ef26410f264c06ce914958f9a42813556" +source = "git+https://github.com/alexcrichton/wasm-tools?branch=component-wast#458426690fdc1a598e9f033fa3c290de3a30cd76" dependencies = [ "wast 45.0.0", ] diff --git a/Cargo.toml b/Cargo.toml index 9cd9ad2013d4..af86a5067935 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,3 +148,12 @@ harness = false [[bench]] name = "call" harness = false + +[patch.crates-io] +wasmparser = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'component-wast' } +wasm-encoder = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'component-wast' } +wasm-mutate = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'component-wast' } +wasm-smith = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'component-wast' } +wasmprinter = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'component-wast' } +wast = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'component-wast' } +wat = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'component-wast' } diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs index 8ca0eeaf5ecb..e6a91e6dfb72 100644 --- a/cranelift/wasm/src/sections_translator.rs +++ b/cranelift/wasm/src/sections_translator.rs @@ -304,10 +304,10 @@ pub fn parse_element_section<'data>( match kind { ElementKind::Active { table_index, - init_expr, + offset_expr, } => { - let mut init_expr_reader = init_expr.get_binary_reader(); - let (base, offset) = match init_expr_reader.read_operator()? { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { Operator::I32Const { value } => (None, value as u32), Operator::GlobalGet { global_index } => { (Some(GlobalIndex::from_u32(global_index)), 0) @@ -354,10 +354,10 @@ pub fn parse_data_section<'data>( match kind { DataKind::Active { memory_index, - init_expr, + offset_expr, } => { - let mut init_expr_reader = init_expr.get_binary_reader(); - let (base, offset) = match init_expr_reader.read_operator()? { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { Operator::I32Const { value } => (None, value as u64), Operator::I64Const { value } => (None, value as u64), Operator::GlobalGet { global_index } => { diff --git a/crates/component-macro/src/lib.rs b/crates/component-macro/src/lib.rs index 9c801a270f17..77b9aa9934ca 100644 --- a/crates/component-macro/src/lib.rs +++ b/crates/component-macro/src/lib.rs @@ -753,11 +753,11 @@ impl Expander for ComponentTypeExpander { let name = rename.unwrap_or_else(|| Literal::string(&ident.to_string())); if let Some(ty) = ty { - abi_list.extend(quote!(<#ty as wasmtime::component::ComponentType>::ABI,)); + abi_list.extend(quote!(Some(<#ty as wasmtime::component::ComponentType>::ABI),)); case_names_and_checks.extend(match style { VariantStyle::Variant => { - quote!((#name, <#ty as wasmtime::component::ComponentType>::typecheck),) + quote!((#name, Some(<#ty as wasmtime::component::ComponentType>::typecheck)),) } VariantStyle::Union => { quote!(<#ty as wasmtime::component::ComponentType>::typecheck,) @@ -780,10 +780,10 @@ impl Expander for ComponentTypeExpander { unique_types.insert(ty); } else { - abi_list.extend(quote!(<() as wasmtime::component::ComponentType>::ABI,)); + abi_list.extend(quote!(None,)); case_names_and_checks.extend(match style { VariantStyle::Variant => { - quote!((#name, <() as wasmtime::component::ComponentType>::typecheck),) + quote!((#name, None),) } VariantStyle::Union => { quote!(<() as wasmtime::component::ComponentType>::typecheck,) @@ -846,7 +846,7 @@ impl Expander for ComponentTypeExpander { } unsafe impl #impl_generics #internal::ComponentVariant for #name #ty_generics #where_clause { - const CASES: &'static [#internal::CanonicalAbiInfo] = &[#abi_list]; + const CASES: &'static [Option<#internal::CanonicalAbiInfo>] = &[#abi_list]; } }; diff --git a/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs b/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs index 2c7fe269ed6b..b43f8cda6c90 100644 --- a/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs +++ b/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs @@ -79,11 +79,11 @@ fn target(module: GenAdapterModule) { let wat = format!( "(component {types} - (type (func {params} {result})) + (type (func {params} {results})) )", types = wat_decls.types, params = wat_decls.params, - result = wat_decls.result, + results = wat_decls.results, ); let wasm = wat::parse_str(&wat).unwrap(); diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs index 0c4e47ee75ae..eab476d94d27 100644 --- a/crates/environ/src/component/types.rs +++ b/crates/environ/src/component/types.rs @@ -94,9 +94,9 @@ indices! { /// Index pointing to an option type in the component model (aka a /// `Option`) pub struct TypeOptionIndex(u32); - /// Index pointing to an expected type in the component model (aka a + /// Index pointing to an result type in the component model (aka a /// `Result`) - pub struct TypeExpectedIndex(u32); + pub struct TypeResultIndex(u32); // ======================================================================== // Index types used to identify modules and components during compilation. @@ -215,7 +215,7 @@ pub struct ComponentTypes { flags: PrimaryMap, unions: PrimaryMap, options: PrimaryMap, - expecteds: PrimaryMap, + results: PrimaryMap, module_types: ModuleTypes, } @@ -229,8 +229,6 @@ impl ComponentTypes { /// Returns the canonical ABI information about the specified type. pub fn canonical_abi(&self, ty: &InterfaceType) -> &CanonicalAbiInfo { match ty { - InterfaceType::Unit => &CanonicalAbiInfo::ZERO, - InterfaceType::U8 | InterfaceType::S8 | InterfaceType::Bool => { &CanonicalAbiInfo::SCALAR1 } @@ -255,7 +253,7 @@ impl ComponentTypes { InterfaceType::Enum(i) => &self[*i].abi, InterfaceType::Union(i) => &self[*i].abi, InterfaceType::Option(i) => &self[*i].abi, - InterfaceType::Expected(i) => &self[*i].abi, + InterfaceType::Result(i) => &self[*i].abi, } } } @@ -284,7 +282,7 @@ impl_index! { impl Index for ComponentTypes { TypeFlags => flags } impl Index for ComponentTypes { TypeUnion => unions } impl Index for ComponentTypes { TypeOption => options } - impl Index for ComponentTypes { TypeExpected => expecteds } + impl Index for ComponentTypes { TypeResult => results } } // Additionally forward anything that can index `ModuleTypes` to `ModuleTypes` @@ -315,7 +313,7 @@ pub struct ComponentTypesBuilder { flags: HashMap, unions: HashMap, options: HashMap, - expecteds: HashMap, + results: HashMap, component_types: ComponentTypes, module_types: ModuleTypesBuilder, @@ -637,7 +635,11 @@ impl ComponentTypesBuilder { .iter() .map(|(name, ty)| (name.map(|s| s.to_string()), self.valtype(ty))) .collect(), - result: self.valtype(&ty.result), + results: ty + .results + .iter() + .map(|(name, ty)| (name.map(|s| s.to_string()), self.valtype(ty))) + .collect(), }; self.add_func_type(ty) } @@ -662,8 +664,8 @@ impl ComponentTypesBuilder { wasmparser::ComponentDefinedType::Option(e) => { InterfaceType::Option(self.option_type(e)) } - wasmparser::ComponentDefinedType::Expected { ok, error } => { - InterfaceType::Expected(self.expected_type(ok, error)) + wasmparser::ComponentDefinedType::Result { ok, err } => { + InterfaceType::Result(self.result_type(ok, err)) } } } @@ -707,15 +709,14 @@ impl ComponentTypesBuilder { assert!(case.refines.is_none()); VariantCase { name: case.name.to_string(), - ty: self.valtype(&case.ty), + ty: case.ty.as_ref().map(|ty| self.valtype(ty)), } }) .collect::>(); - let (info, abi) = VariantInfo::new( - cases - .iter() - .map(|c| self.component_types.canonical_abi(&c.ty)), - ); + let (info, abi) = VariantInfo::new(cases.iter().map(|c| { + c.ty.as_ref() + .map(|ty| self.component_types.canonical_abi(ty)) + })); self.add_variant_type(TypeVariant { cases, abi, info }) } @@ -742,11 +743,7 @@ impl ComponentTypesBuilder { fn enum_type(&mut self, variants: &[&str]) -> TypeEnumIndex { let names = variants.iter().map(|s| s.to_string()).collect::>(); - let (info, abi) = VariantInfo::new( - names - .iter() - .map(|_| self.component_types.canonical_abi(&InterfaceType::Unit)), - ); + let (info, abi) = VariantInfo::new(names.iter().map(|_| None)); self.add_enum_type(TypeEnum { names, abi, info }) } @@ -755,32 +752,32 @@ impl ComponentTypesBuilder { .iter() .map(|ty| self.valtype(ty)) .collect::>(); - let (info, abi) = - VariantInfo::new(types.iter().map(|t| self.component_types.canonical_abi(t))); + let (info, abi) = VariantInfo::new( + types + .iter() + .map(|t| Some(self.component_types.canonical_abi(t))), + ); self.add_union_type(TypeUnion { types, abi, info }) } fn option_type(&mut self, ty: &wasmparser::ComponentValType) -> TypeOptionIndex { let ty = self.valtype(ty); - let (info, abi) = VariantInfo::new([ - self.component_types.canonical_abi(&InterfaceType::Unit), - self.component_types.canonical_abi(&ty), - ]); + let (info, abi) = VariantInfo::new([None, Some(self.component_types.canonical_abi(&ty))]); self.add_option_type(TypeOption { ty, abi, info }) } - fn expected_type( + fn result_type( &mut self, - ok: &wasmparser::ComponentValType, - err: &wasmparser::ComponentValType, - ) -> TypeExpectedIndex { - let ok = self.valtype(ok); - let err = self.valtype(err); + ok: &Option, + err: &Option, + ) -> TypeResultIndex { + let ok = ok.as_ref().map(|ty| self.valtype(ty)); + let err = err.as_ref().map(|ty| self.valtype(ty)); let (info, abi) = VariantInfo::new([ - self.component_types.canonical_abi(&ok), - self.component_types.canonical_abi(&err), + ok.as_ref().map(|t| self.component_types.canonical_abi(t)), + err.as_ref().map(|t| self.component_types.canonical_abi(t)), ]); - self.add_expected_type(TypeExpected { ok, err, abi, info }) + self.add_result_type(TypeResult { ok, err, abi, info }) } /// Interns a new function type within this type information. @@ -823,12 +820,12 @@ impl ComponentTypesBuilder { intern_and_fill_flat_types!(self, options, ty) } - /// Interns a new expected type within this type information. - pub fn add_expected_type(&mut self, ty: TypeExpected) -> TypeExpectedIndex { - intern_and_fill_flat_types!(self, expecteds, ty) + /// Interns a new result type within this type information. + pub fn add_result_type(&mut self, ty: TypeResult) -> TypeResultIndex { + intern_and_fill_flat_types!(self, results, ty) } - /// Interns a new expected type within this type information. + /// Interns a new type within this type information. pub fn add_interface_type(&mut self, ty: InterfaceType) -> TypeInterfaceIndex { intern( &mut self.interface_types, @@ -849,7 +846,6 @@ impl ComponentTypesBuilder { /// in the canonical abi. pub fn flat_types(&self, ty: &InterfaceType) -> Option> { match ty { - InterfaceType::Unit => Some(FlatTypes::EMPTY), InterfaceType::U8 | InterfaceType::S8 | InterfaceType::Bool @@ -870,7 +866,7 @@ impl ComponentTypesBuilder { InterfaceType::Enum(i) => self.flat.enums[*i].as_flat_types(), InterfaceType::Union(i) => self.flat.unions[*i].as_flat_types(), InterfaceType::Option(i) => self.flat.options[*i].as_flat_types(), - InterfaceType::Expected(i) => self.flat.expecteds[*i].as_flat_types(), + InterfaceType::Result(i) => self.flat.results[*i].as_flat_types(), } } } @@ -973,8 +969,8 @@ pub struct TypeFunc { /// The list of optionally named parameters for this function, and their /// types. pub params: Box<[(Option, InterfaceType)]>, - /// The return value of this function. - pub result: InterfaceType, + /// The return values of this function. + pub results: Box<[(Option, InterfaceType)]>, } /// All possible interface types that values can have. @@ -986,7 +982,6 @@ pub struct TypeFunc { #[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq, Debug)] #[allow(missing_docs)] pub enum InterfaceType { - Unit, Bool, S8, U8, @@ -1008,13 +1003,12 @@ pub enum InterfaceType { Enum(TypeEnumIndex), Union(TypeUnionIndex), Option(TypeOptionIndex), - Expected(TypeExpectedIndex), + Result(TypeResultIndex), } impl From<&wasmparser::PrimitiveValType> for InterfaceType { fn from(ty: &wasmparser::PrimitiveValType) -> InterfaceType { match ty { - wasmparser::PrimitiveValType::Unit => InterfaceType::Unit, wasmparser::PrimitiveValType::Bool => InterfaceType::Bool, wasmparser::PrimitiveValType::S8 => InterfaceType::S8, wasmparser::PrimitiveValType::U8 => InterfaceType::U8, @@ -1080,7 +1074,7 @@ const fn max(a: u32, b: u32) -> u32 { impl CanonicalAbiInfo { /// ABI information for zero-sized types. - pub const ZERO: CanonicalAbiInfo = CanonicalAbiInfo { + const ZERO: CanonicalAbiInfo = CanonicalAbiInfo { size32: 0, align32: 1, size64: 0, @@ -1204,7 +1198,7 @@ impl CanonicalAbiInfo { fn variant<'a, I>(cases: I) -> CanonicalAbiInfo where - I: IntoIterator, + I: IntoIterator>, I::IntoIter: ExactSizeIterator, { // NB: this is basically a duplicate definition of @@ -1218,11 +1212,13 @@ impl CanonicalAbiInfo { let mut max_align64 = discrim_size; let mut max_case_count = Some(0); for case in cases { - max_size32 = max_size32.max(case.size32); - max_align32 = max_align32.max(case.align32); - max_size64 = max_size64.max(case.size64); - max_align64 = max_align64.max(case.align64); - max_case_count = max_flat(max_case_count, case.flat_count); + if let Some(case) = case { + max_size32 = max_size32.max(case.size32); + max_align32 = max_align32.max(case.align32); + max_size64 = max_size64.max(case.size64); + max_align64 = max_align64.max(case.align64); + max_case_count = max_flat(max_case_count, case.flat_count); + } } CanonicalAbiInfo { size32: align_to( @@ -1240,7 +1236,7 @@ impl CanonicalAbiInfo { } /// Same as `CanonicalAbiInfo::variant` but `const`-safe - pub const fn variant_static(cases: &[CanonicalAbiInfo]) -> CanonicalAbiInfo { + pub const fn variant_static(cases: &[Option]) -> CanonicalAbiInfo { // NB: this is basically a duplicate definition of // `CanonicalAbiInfo::variant`, these should be kept in sync. @@ -1256,11 +1252,13 @@ impl CanonicalAbiInfo { let mut i = 0; while i < cases.len() { let case = &cases[i]; - max_size32 = max(max_size32, case.size32); - max_align32 = max(max_align32, case.align32); - max_size64 = max(max_size64, case.size64); - max_align64 = max(max_align64, case.align64); - max_case_count = max_flat(max_case_count, case.flat_count); + if let Some(case) = case { + max_size32 = max(max_size32, case.size32); + max_align32 = max(max_align32, case.align32); + max_size64 = max(max_size64, case.size64); + max_align64 = max(max_align64, case.align64); + max_case_count = max_flat(max_case_count, case.flat_count); + } i += 1; } CanonicalAbiInfo { @@ -1309,7 +1307,7 @@ impl VariantInfo { /// cases. pub fn new<'a, I>(cases: I) -> (VariantInfo, CanonicalAbiInfo) where - I: IntoIterator, + I: IntoIterator>, I::IntoIter: ExactSizeIterator, { let cases = cases.into_iter(); @@ -1325,7 +1323,7 @@ impl VariantInfo { ) } /// TODO - pub const fn new_static(cases: &[CanonicalAbiInfo]) -> VariantInfo { + pub const fn new_static(cases: &[Option]) -> VariantInfo { let size = match DiscriminantSize::from_count(cases.len()) { Some(size) => size, None => unreachable!(), @@ -1404,8 +1402,8 @@ pub struct TypeVariant { pub struct VariantCase { /// Name of the variant, unique amongst all cases in a variant. pub name: String, - /// Type associated with this payload, maybe `Unit`. - pub ty: InterfaceType, + /// Optional type associated with this payload. + pub ty: Option, } /// Shape of a "tuple" type in interface types. @@ -1473,13 +1471,13 @@ pub struct TypeOption { pub info: VariantInfo, } -/// Shape of an "expected" interface type. +/// Shape of a "result" interface type. #[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] -pub struct TypeExpected { +pub struct TypeResult { /// The `T` in `Result` - pub ok: InterfaceType, + pub ok: Option, /// The `E` in `Result` - pub err: InterfaceType, + pub err: Option, /// Byte information about this type in the canonical ABI. pub abi: CanonicalAbiInfo, /// Byte information about this variant type. @@ -1531,7 +1529,6 @@ pub struct FlatTypes<'a> { #[allow(missing_docs)] impl FlatTypes<'_> { - pub const EMPTY: FlatTypes<'static> = FlatTypes::new(&[]); pub const I32: FlatTypes<'static> = FlatTypes::new(&[FlatType::I32]); pub const I64: FlatTypes<'static> = FlatTypes::new(&[FlatType::I64]); pub const F32: FlatTypes<'static> = FlatTypes::new(&[FlatType::F32]); @@ -1578,7 +1575,7 @@ struct FlatTypesCache { flags: PrimaryMap, unions: PrimaryMap, options: PrimaryMap, - expecteds: PrimaryMap, + results: PrimaryMap, } struct FlatTypesStorage { @@ -1662,22 +1659,33 @@ impl FlatTypesStorage { /// Builds up the flat types used to represent a `variant` which notably /// handles "join"ing types together so each case is representable as a /// single flat list of types. + /// + /// The iterator item is: + /// + /// * `None` - no payload for this case + /// * `Some(None)` - this case has a payload but can't be represented with + /// flat types + /// * `Some(Some(types))` - this case has a payload and is represented with + /// the types specified in the flat representation. fn build_variant<'a, I>(&mut self, cases: I) where - I: IntoIterator>>, + I: IntoIterator>>>, { let cases = cases.into_iter(); self.push(FlatType::I32, FlatType::I32); for ty in cases { let types = match ty { - Some(types) => types, + Some(Some(types)) => types, // If this case isn't representable with a flat list of types // then this variant also isn't representable. - None => { + Some(None) => { self.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap(); return; } + // If this case doesn't have a payload then it doesn't change + // whether this is representable or not. + None => continue, }; // If the case used all of the flat types then the discriminant // added for this variant means that this variant is no longer @@ -1739,19 +1747,26 @@ impl FlatTypesStorage { } fn variants(&mut self, types: &ComponentTypesBuilder, ty: &TypeVariant) { - self.build_variant(ty.cases.iter().map(|c| types.flat_types(&c.ty))) + self.build_variant( + ty.cases + .iter() + .map(|c| c.ty.as_ref().map(|ty| types.flat_types(ty))), + ) } fn unions(&mut self, types: &ComponentTypesBuilder, ty: &TypeUnion) { - self.build_variant(ty.types.iter().map(|t| types.flat_types(t))) + self.build_variant(ty.types.iter().map(|t| Some(types.flat_types(t)))) } - fn expecteds(&mut self, types: &ComponentTypesBuilder, ty: &TypeExpected) { - self.build_variant([types.flat_types(&ty.ok), types.flat_types(&ty.err)]); + fn results(&mut self, types: &ComponentTypesBuilder, ty: &TypeResult) { + self.build_variant([ + ty.ok.as_ref().map(|ty| types.flat_types(ty)), + ty.err.as_ref().map(|ty| types.flat_types(ty)), + ]) } fn options(&mut self, types: &ComponentTypesBuilder, ty: &TypeOption) { - self.build_variant([Some(FlatTypes::EMPTY), types.flat_types(&ty.ty)]); + self.build_variant([None, Some(types.flat_types(&ty.ty))]); } } diff --git a/crates/environ/src/fact/signature.rs b/crates/environ/src/fact/signature.rs index c93836c1fa89..24a1fdd523e2 100644 --- a/crates/environ/src/fact/signature.rs +++ b/crates/environ/src/fact/signature.rs @@ -49,7 +49,11 @@ impl ComponentTypesBuilder { }; let mut results_indirect = false; - let results = match self.flatten_types(&options.options, MAX_FLAT_RESULTS, [ty.result]) { + let results = match self.flatten_types( + &options.options, + MAX_FLAT_RESULTS, + ty.results.iter().map(|(_, ty)| *ty), + ) { Some(list) => list, None => { results_indirect = true; diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index a058ebeb98e3..406bcd732a75 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -17,7 +17,7 @@ use crate::component::{ CanonicalAbiInfo, ComponentTypesBuilder, InterfaceType, StringEncoding, TypeEnumIndex, - TypeExpectedIndex, TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, + TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, TypeResultIndex, TypeTupleIndex, TypeUnionIndex, TypeVariantIndex, VariantInfo, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, }; @@ -345,17 +345,19 @@ impl Compiler<'_, '_> { param_locals: &[(u32, ValType)], result_locals: &[(u32, ValType)], ) { - let src_ty = self.types[adapter.lift.ty].result; - let dst_ty = self.types[adapter.lower.ty].result; + let src_tys = &self.types[adapter.lift.ty].results; + let src_tys = src_tys.iter().map(|(_, ty)| *ty).collect::>(); + let dst_tys = &self.types[adapter.lower.ty].results; + let dst_tys = dst_tys.iter().map(|(_, ty)| *ty).collect::>(); let lift_opts = &adapter.lift.options; let lower_opts = &adapter.lower.options; - let src_flat = self - .types - .flatten_types(lift_opts, MAX_FLAT_RESULTS, [src_ty]); - let dst_flat = self - .types - .flatten_types(lower_opts, MAX_FLAT_RESULTS, [dst_ty]); + let src_flat = + self.types + .flatten_types(lift_opts, MAX_FLAT_RESULTS, src_tys.iter().copied()); + let dst_flat = + self.types + .flatten_types(lower_opts, MAX_FLAT_RESULTS, dst_tys.iter().copied()); let src = if src_flat.is_some() { Source::Stack(Stack { @@ -367,7 +369,11 @@ impl Compiler<'_, '_> { // return value of the function itself. The imported function will // return a linear memory address at which the values can be read // from. - let align = self.types.align(lift_opts, &src_ty); + let align = src_tys + .iter() + .map(|t| self.types.align(lift_opts, t)) + .max() + .unwrap_or(1); assert_eq!(result_locals.len(), 1); let (addr, ty) = result_locals[0]; assert_eq!(ty, lift_opts.ptr()); @@ -380,13 +386,25 @@ impl Compiler<'_, '_> { // This is slightly different than `translate_params` where the // return pointer was provided by the caller of this function // meaning the last parameter local is a pointer into linear memory. - let align = self.types.align(lower_opts, &dst_ty); + let align = dst_tys + .iter() + .map(|t| self.types.align(lower_opts, t)) + .max() + .unwrap_or(1); let (addr, ty) = *param_locals.last().expect("no retptr"); assert_eq!(ty, lower_opts.ptr()); Destination::Memory(self.memory_operand(lower_opts, TempLocal::new(addr, ty), align)) }; - self.translate(&src_ty, &src, &dst_ty, &dst); + let srcs = src + .record_field_srcs(self.types, src_tys.iter().copied()) + .zip(src_tys.iter()); + let dsts = dst + .record_field_dsts(self.types, dst_tys.iter().copied()) + .zip(dst_tys.iter()); + for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) { + self.translate(&src_ty, &src, &dst_ty, &dst); + } } fn translate( @@ -406,8 +424,7 @@ impl Compiler<'_, '_> { // Classify the source type as "primitive" or not as a heuristic to // whether the translation should be split out into a helper function. let src_primitive = match src_ty { - InterfaceType::Unit - | InterfaceType::Bool + InterfaceType::Bool | InterfaceType::U8 | InterfaceType::S8 | InterfaceType::U16 @@ -429,7 +446,7 @@ impl Compiler<'_, '_> { | InterfaceType::Union(_) | InterfaceType::Enum(_) | InterfaceType::Option(_) - | InterfaceType::Expected(_) => false, + | InterfaceType::Result(_) => false, }; let top_level = mem::replace(&mut self.top_level_translate, false); @@ -440,10 +457,10 @@ impl Compiler<'_, '_> { // were translated inline then this could get arbitrarily large // // (type $level0 (list u8)) - // (type $level1 (expected $level0 $level0)) - // (type $level2 (expected $level1 $level1)) - // (type $level3 (expected $level2 $level2)) - // (type $level4 (expected $level3 $level3)) + // (type $level1 (result $level0 $level0)) + // (type $level2 (result $level1 $level1)) + // (type $level3 (result $level2 $level2)) + // (type $level4 (result $level3 $level3)) // ;; ... // // If everything we inlined then translation of `$level0` would appear @@ -505,7 +522,6 @@ impl Compiler<'_, '_> { } } match src_ty { - InterfaceType::Unit => self.translate_unit(src, dst_ty, dst), InterfaceType::Bool => self.translate_bool(src, dst_ty, dst), InterfaceType::U8 => self.translate_u8(src, dst_ty, dst), InterfaceType::S8 => self.translate_s8(src, dst_ty, dst), @@ -527,18 +543,12 @@ impl Compiler<'_, '_> { InterfaceType::Union(u) => self.translate_union(*u, src, dst_ty, dst), InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst), InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst), - InterfaceType::Expected(t) => self.translate_expected(*t, src, dst_ty, dst), + InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst), } self.top_level_translate = top_level; } - fn translate_unit(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) { - // TODO: subtyping - assert!(matches!(dst_ty, InterfaceType::Unit)); - drop((src, dst)); - } - fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) { // TODO: subtyping assert!(matches!(dst_ty, InterfaceType::Bool)); @@ -1996,8 +2006,8 @@ impl Compiler<'_, '_> { _ => panic!("expected a variant"), }; - let src_info = variant_info(self.types, src_ty.cases.iter().map(|c| c.ty)); - let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|c| c.ty)); + let src_info = variant_info(self.types, src_ty.cases.iter().map(|c| c.ty.as_ref())); + let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|c| c.ty.as_ref())); let iter = src_ty.cases.iter().enumerate().map(|(src_i, src_case)| { let dst_i = dst_ty @@ -2010,9 +2020,9 @@ impl Compiler<'_, '_> { let dst_i = u32::try_from(dst_i).unwrap(); VariantCase { src_i, - src_ty: &src_case.ty, + src_ty: src_case.ty.as_ref(), dst_i, - dst_ty: &dst_case.ty, + dst_ty: dst_case.ty.as_ref(), } }); self.convert_variant(src, &src_info, dst, &dst_info, iter); @@ -2031,8 +2041,8 @@ impl Compiler<'_, '_> { _ => panic!("expected an option"), }; assert_eq!(src_ty.types.len(), dst_ty.types.len()); - let src_info = variant_info(self.types, src_ty.types.iter().copied()); - let dst_info = variant_info(self.types, dst_ty.types.iter().copied()); + let src_info = variant_info(self.types, src_ty.types.iter().map(Some)); + let dst_info = variant_info(self.types, dst_ty.types.iter().map(Some)); self.convert_variant( src, @@ -2049,8 +2059,8 @@ impl Compiler<'_, '_> { VariantCase { src_i: i, dst_i: i, - src_ty, - dst_ty, + src_ty: Some(src_ty), + dst_ty: Some(dst_ty), } }), ); @@ -2068,10 +2078,9 @@ impl Compiler<'_, '_> { InterfaceType::Enum(t) => &self.types[*t], _ => panic!("expected an option"), }; - let src_info = variant_info(self.types, src_ty.names.iter().map(|_| InterfaceType::Unit)); - let dst_info = variant_info(self.types, dst_ty.names.iter().map(|_| InterfaceType::Unit)); + let src_info = variant_info(self.types, src_ty.names.iter().map(|_| None)); + let dst_info = variant_info(self.types, dst_ty.names.iter().map(|_| None)); - let unit = &InterfaceType::Unit; self.convert_variant( src, &src_info, @@ -2084,8 +2093,8 @@ impl Compiler<'_, '_> { VariantCase { src_i, dst_i, - src_ty: unit, - dst_ty: unit, + src_ty: None, + dst_ty: None, } }), ); @@ -2103,9 +2112,11 @@ impl Compiler<'_, '_> { InterfaceType::Option(t) => &self.types[*t].ty, _ => panic!("expected an option"), }; + let src_ty = Some(src_ty); + let dst_ty = Some(dst_ty); - let src_info = variant_info(self.types, [InterfaceType::Unit, *src_ty]); - let dst_info = variant_info(self.types, [InterfaceType::Unit, *dst_ty]); + let src_info = variant_info(self.types, [None, src_ty]); + let dst_info = variant_info(self.types, [None, dst_ty]); self.convert_variant( src, @@ -2116,8 +2127,8 @@ impl Compiler<'_, '_> { VariantCase { src_i: 0, dst_i: 0, - src_ty: &InterfaceType::Unit, - dst_ty: &InterfaceType::Unit, + src_ty: None, + dst_ty: None, }, VariantCase { src_i: 1, @@ -2130,21 +2141,21 @@ impl Compiler<'_, '_> { ); } - fn translate_expected( + fn translate_result( &mut self, - src_ty: TypeExpectedIndex, + src_ty: TypeResultIndex, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination, ) { let src_ty = &self.types[src_ty]; let dst_ty = match dst_ty { - InterfaceType::Expected(t) => &self.types[*t], - _ => panic!("expected an expected"), + InterfaceType::Result(t) => &self.types[*t], + _ => panic!("expected a result"), }; - let src_info = variant_info(self.types, [src_ty.ok, src_ty.err]); - let dst_info = variant_info(self.types, [dst_ty.ok, dst_ty.err]); + let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]); + let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]); self.convert_variant( src, @@ -2155,14 +2166,14 @@ impl Compiler<'_, '_> { VariantCase { src_i: 0, dst_i: 0, - src_ty: &src_ty.ok, - dst_ty: &dst_ty.ok, + src_ty: src_ty.ok.as_ref(), + dst_ty: dst_ty.ok.as_ref(), }, VariantCase { src_i: 1, dst_i: 1, - src_ty: &src_ty.err, - dst_ty: &dst_ty.err, + src_ty: src_ty.err.as_ref(), + dst_ty: dst_ty.err.as_ref(), }, ] .into_iter(), @@ -2254,11 +2265,18 @@ impl Compiler<'_, '_> { }, } - // Translate the payload of this case using the various types from - // the dst/src. let src_payload = src.payload_src(self.types, src_info, src_ty); let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty); - self.translate(src_ty, &src_payload, dst_ty, &dst_payload); + + // Translate the payload of this case using the various types from + // the dst/src. + match (src_ty, dst_ty) { + (Some(src_ty), Some(dst_ty)) => { + self.translate(src_ty, &src_payload, dst_ty, &dst_payload); + } + (None, None) => {} + _ => unimplemented!(), + } // If the results of this translation were placed on the stack then // the stack values may need to be padded with more zeros due to @@ -2824,11 +2842,14 @@ impl<'a> Source<'a> { &self, types: &ComponentTypesBuilder, info: &VariantInfo, - case: &InterfaceType, + case: Option<&InterfaceType>, ) -> Source<'a> { match self { Source::Stack(s) => { - let flat_len = types.flat_types(case).unwrap().len(); + let flat_len = match case { + Some(case) => types.flat_types(case).unwrap().len(), + None => 0, + }; Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len)) } Source::Memory(mem) => { @@ -2879,11 +2900,14 @@ impl<'a> Destination<'a> { &self, types: &ComponentTypesBuilder, info: &VariantInfo, - case: &InterfaceType, + case: Option<&InterfaceType>, ) -> Destination { match self { Destination::Stack(s, opts) => { - let flat_len = types.flat_types(case).unwrap().len(); + let flat_len = match case { + Some(case) => types.flat_types(case).unwrap().len(), + None => 0, + }; Destination::Stack(&s[1..][..flat_len], opts) } Destination::Memory(mem) => { @@ -2949,17 +2973,22 @@ impl<'a> Stack<'a> { struct VariantCase<'a> { src_i: u32, - src_ty: &'a InterfaceType, + src_ty: Option<&'a InterfaceType>, dst_i: u32, - dst_ty: &'a InterfaceType, + dst_ty: Option<&'a InterfaceType>, } -fn variant_info(types: &ComponentTypesBuilder, cases: I) -> VariantInfo +fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo where - I: IntoIterator, + I: IntoIterator>, I::IntoIter: ExactSizeIterator, { - VariantInfo::new(cases.into_iter().map(|i| types.canonical_abi(&i))).0 + VariantInfo::new( + cases + .into_iter() + .map(|ty| ty.map(|ty| types.canonical_abi(ty))), + ) + .0 } enum MallocSize { diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs index 7429a348ab93..b029084aeb48 100644 --- a/crates/environ/src/module_environ.rs +++ b/crates/environ/src/module_environ.rs @@ -429,11 +429,11 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { match kind { ElementKind::Active { table_index, - init_expr, + offset_expr, } => { let table_index = TableIndex::from_u32(table_index); - let mut init_expr_reader = init_expr.get_binary_reader(); - let (base, offset) = match init_expr_reader.read_operator()? { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { Operator::I32Const { value } => (None, value as u32), Operator::GlobalGet { global_index } => { (Some(GlobalIndex::from_u32(global_index)), 0) @@ -547,12 +547,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> { match kind { DataKind::Active { memory_index, - init_expr, + offset_expr, } => { let range = mk_range(&mut self.result.total_data)?; let memory_index = MemoryIndex::from_u32(memory_index); - let mut init_expr_reader = init_expr.get_binary_reader(); - let (base, offset) = match init_expr_reader.read_operator()? { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { Operator::I32Const { value } => (None, value as u64), Operator::I64Const { value } => (None, value as u64), Operator::GlobalGet { global_index } => { diff --git a/crates/fuzzing/src/generators/component_types.rs b/crates/fuzzing/src/generators/component_types.rs index 1e78645a9dbc..133b990fc29e 100644 --- a/crates/fuzzing/src/generators/component_types.rs +++ b/crates/fuzzing/src/generators/component_types.rs @@ -11,7 +11,7 @@ use component_fuzz_util::{Declarations, EXPORT_FUNCTION, IMPORT_FUNCTION}; use std::any::Any; use std::fmt::Debug; use std::ops::ControlFlow; -use wasmtime::component::{self, Component, Lift, Linker, Lower, Val}; +use wasmtime::component::{self, Component, ComponentNamedList, Lift, Linker, Lower, Val}; use wasmtime::{Config, Engine, Store, StoreContextMut}; /// Minimum length of an arbitrary list value generated for a test case @@ -25,7 +25,6 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar use component::Type; Ok(match ty { - Type::Unit => Val::Unit, Type::Bool => Val::Bool(input.arbitrary()?), Type::S8 => Val::S8(input.arbitrary()?), Type::U8 => Val::U8(input.arbitrary()?), @@ -66,18 +65,18 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar ) .unwrap(), Type::Variant(variant) => { - let mut cases = variant.cases(); - let discriminant = input.int_in_range(0..=cases.len() - 1)?; - variant - .new_val( - &format!("C{discriminant}"), - arbitrary_val(&cases.nth(discriminant).unwrap().ty, input)?, - ) - .unwrap() + let cases = variant.cases().collect::>(); + let case = input.choose(&cases)?; + let payload = match &case.ty { + Some(ty) => Some(arbitrary_val(ty, input)?), + None => None, + }; + variant.new_val(case.name, payload).unwrap() } Type::Enum(en) => { - let discriminant = input.int_in_range(0..=en.names().len() - 1)?; - en.new_val(&format!("C{discriminant}")).unwrap() + let names = en.names().collect::>(); + let name = input.choose(&names)?; + en.new_val(name).unwrap() } Type::Union(un) => { let mut types = un.types(); @@ -98,12 +97,18 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar }) .unwrap() } - Type::Expected(expected) => { + Type::Result(result) => { let discriminant = input.int_in_range(0..=1)?; - expected + result .new_val(match discriminant { - 0 => Ok(arbitrary_val(&expected.ok(), input)?), - 1 => Err(arbitrary_val(&expected.err(), input)?), + 0 => Ok(match result.ok() { + Some(ty) => Some(arbitrary_val(&ty, input)?), + None => None, + }), + 1 => Err(match result.err() { + Some(ty) => Some(arbitrary_val(&ty, input)?), + None => None, + }), _ => unreachable!(), }) .unwrap() @@ -135,7 +140,7 @@ macro_rules! define_static_api_test { ) -> arbitrary::Result<()> where $($param: Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static,)* - R: Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static + R: ComponentNamedList + Lift + Lower + Clone + PartialEq + Debug + Arbitrary<'a> + 'static { crate::init_fuzzing(); diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index 0bf661749545..2ddc0c31e585 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -1090,7 +1090,7 @@ pub fn dynamic_component_api_target(input: &mut arbitrary::Unstructured) -> arbi let mut config = component_test_util::config(); config.debug_adapter_modules(input.arbitrary()?); let engine = Engine::new(&config).unwrap(); - let mut store = Store::new(&engine, (Box::new([]) as Box<[Val]>, None)); + let mut store = Store::new(&engine, (Vec::new(), None)); let wat = case.declarations().make_component(); let wat = wat.as_bytes(); log_wasm(wat); @@ -1100,39 +1100,46 @@ pub fn dynamic_component_api_target(input: &mut arbitrary::Unstructured) -> arbi linker .root() .func_new(&component, IMPORT_FUNCTION, { - move |cx: StoreContextMut<'_, (Box<[Val]>, Option)>, args: &[Val]| -> Result { - log::trace!("received arguments {args:?}"); - let (expected_args, result) = cx.data(); - assert_eq!(args.len(), expected_args.len()); - for (expected, actual) in expected_args.iter().zip(args) { + move |mut cx: StoreContextMut<'_, (Vec, Option>)>, + params: &[Val], + results: &mut [Val]| + -> Result<()> { + log::trace!("received params {params:?}"); + let (expected_args, expected_results) = cx.data_mut(); + assert_eq!(params.len(), expected_args.len()); + for (expected, actual) in expected_args.iter().zip(params) { assert_eq!(expected, actual); } - let result = result.as_ref().unwrap().clone(); - log::trace!("returning result {result:?}"); - Ok(result) + results.clone_from_slice(&expected_results.take().unwrap()); + log::trace!("returning results {results:?}"); + Ok(()) } }) .unwrap(); let instance = linker.instantiate(&mut store, &component).unwrap(); let func = instance.get_func(&mut store, EXPORT_FUNCTION).unwrap(); - let params = func.params(&store); - let result = func.result(&store); + let param_tys = func.params(&store); + let result_tys = func.results(&store); while input.arbitrary()? { - let args = params + let params = param_tys .iter() .map(|ty| component_types::arbitrary_val(ty, input)) - .collect::>>()?; - - let result = component_types::arbitrary_val(&result, input)?; + .collect::>>()?; + let results = result_tys + .iter() + .map(|ty| component_types::arbitrary_val(ty, input)) + .collect::>>()?; - *store.data_mut() = (args.clone(), Some(result.clone())); + *store.data_mut() = (params.clone(), Some(results.clone())); - log::trace!("passing args {args:?}"); - let actual = func.call_and_post_return(&mut store, &args).unwrap(); - log::trace!("received return {actual:?}"); - assert_eq!(actual, result); + log::trace!("passing params {params:?}"); + let mut actual = vec![Val::Bool(false); results.len()]; + func.call_and_post_return(&mut store, ¶ms, &mut actual) + .unwrap(); + log::trace!("received results {actual:?}"); + assert_eq!(actual, results); } Ok(()) diff --git a/crates/misc/component-fuzz-util/src/lib.rs b/crates/misc/component-fuzz-util/src/lib.rs index 8387d29d9c13..7c6509d40449 100644 --- a/crates/misc/component-fuzz-util/src/lib.rs +++ b/crates/misc/component-fuzz-util/src/lib.rs @@ -17,7 +17,7 @@ use wasmtime_component_util::{DiscriminantSize, FlagsSize, REALLOC_AND_FREE}; const MAX_FLAT_PARAMS: usize = 16; const MAX_FLAT_RESULTS: usize = 1; -const MAX_ARITY: usize = 5; +const MAX_ARITY: u32 = 5; /// The name of the imported host function which the generated component will call pub const IMPORT_FUNCTION: &str = "echo"; @@ -57,7 +57,7 @@ impl fmt::Display for CoreType { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UsizeInRange(usize); impl UsizeInRange { @@ -74,7 +74,7 @@ impl<'a, const L: usize, const H: usize> Arbitrary<'a> for UsizeInRange { /// Wraps a `Box<[T]>` and provides an `Arbitrary` implementation that always generates slices of length less than /// or equal to the longest tuple for which Wasmtime generates a `ComponentType` impl -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct VecInRange(Vec); impl<'a, T: Arbitrary<'a>, const L: u32, const H: u32> Arbitrary<'a> for VecInRange { @@ -98,9 +98,8 @@ impl Deref for VecInRange { /// Represents a component model interface type #[allow(missing_docs)] -#[derive(Arbitrary, Debug)] +#[derive(Arbitrary, Debug, Clone)] pub enum Type { - Unit, Bool, S8, U8, @@ -129,12 +128,15 @@ pub enum Type { // Like records, allow a good number of variants, but variants require at // least one case. - Variant(VecInRange), + Variant(VecInRange, 1, 200>), Enum(UsizeInRange<1, 257>), Union(VecInRange), Option(Box), - Expected { ok: Box, err: Box }, + Result { + ok: Option>, + err: Option>, + }, // Generate 0 flags all the way up to 65 flags which exercises the 0 to // 3 x u32 cases. @@ -147,10 +149,14 @@ fn lower_record<'a>(types: impl Iterator, vec: &mut Vec(types: impl Iterator, vec: &mut Vec) { +fn lower_variant<'a>(types: impl Iterator>, vec: &mut Vec) { vec.push(CoreType::I32); let offset = vec.len(); for ty in types { + let ty = match ty { + Some(ty) => ty, + None => continue, + }; for (index, ty) in ty.lowered().iter().enumerate() { let index = offset + index; if index < vec.len() { @@ -184,7 +190,6 @@ impl Type { fn lower(&self, vec: &mut Vec) { match self { - Type::Unit => (), Type::Bool | Type::U8 | Type::S8 @@ -203,9 +208,12 @@ impl Type { } Type::Record(types) => lower_record(types.iter(), vec), Type::Tuple(types) => lower_record(types.0.iter(), vec), - Type::Variant(types) | Type::Union(types) => lower_variant(types.0.iter(), vec), - Type::Option(ty) => lower_variant([&Type::Unit, ty].into_iter(), vec), - Type::Expected { ok, err } => lower_variant([ok.deref(), err].into_iter(), vec), + Type::Variant(types) => lower_variant(types.0.iter().map(|t| t.as_ref()), vec), + Type::Union(types) => lower_variant(types.0.iter().map(Some), vec), + Type::Option(ty) => lower_variant([None, Some(&**ty)].into_iter(), vec), + Type::Result { ok, err } => { + lower_variant([ok.as_deref(), err.as_deref()].into_iter(), vec) + } Type::Flags(count) => { vec.extend(iter::repeat(CoreType::I32).take(u32_count_from_flag_count(count.0))) } @@ -214,11 +222,6 @@ impl Type { fn size_and_alignment(&self) -> SizeAndAlignment { match self { - Type::Unit => SizeAndAlignment { - size: 0, - alignment: 1, - }, - Type::Bool | Type::S8 | Type::U8 => SizeAndAlignment { size: 1, alignment: 1, @@ -248,13 +251,16 @@ impl Type { Type::Tuple(types) => record_size_and_alignment(types.0.iter()), - Type::Variant(types) | Type::Union(types) => variant_size_and_alignment(types.0.iter()), + Type::Variant(types) => variant_size_and_alignment(types.0.iter().map(|t| t.as_ref())), + Type::Union(types) => variant_size_and_alignment(types.0.iter().map(Some)), - Type::Enum(count) => variant_size_and_alignment((0..count.0).map(|_| &Type::Unit)), + Type::Enum(count) => variant_size_and_alignment((0..count.0).map(|_| None)), - Type::Option(ty) => variant_size_and_alignment([&Type::Unit, ty].into_iter()), + Type::Option(ty) => variant_size_and_alignment([None, Some(&**ty)].into_iter()), - Type::Expected { ok, err } => variant_size_and_alignment([ok.deref(), err].into_iter()), + Type::Result { ok, err } => { + variant_size_and_alignment([ok.as_deref(), err.as_deref()].into_iter()) + } Type::Flags(count) => match FlagsSize::from_count(count.0) { FlagsSize::Size0 => SizeAndAlignment { @@ -299,15 +305,17 @@ fn record_size_and_alignment<'a>(types: impl Iterator) -> SizeA } fn variant_size_and_alignment<'a>( - types: impl ExactSizeIterator, + types: impl ExactSizeIterator>, ) -> SizeAndAlignment { let discriminant_size = DiscriminantSize::from_count(types.len()).unwrap(); let mut alignment = u32::from(discriminant_size); let mut size = 0; for ty in types { - let size_and_alignment = ty.size_and_alignment(); - alignment = alignment.max(size_and_alignment.alignment); - size = size.max(size_and_alignment.size); + if let Some(ty) = ty { + let size_and_alignment = ty.size_and_alignment(); + alignment = alignment.max(size_and_alignment.alignment); + size = size.max(size_and_alignment.size); + } } SizeAndAlignment { @@ -319,12 +327,15 @@ fn variant_size_and_alignment<'a>( } } -fn make_import_and_export(params: &[Type], result: &Type) -> String { +fn make_import_and_export(params: &[Type], results: &[Type]) -> String { let params_lowered = params .iter() .flat_map(|ty| ty.lowered()) .collect::>(); - let result_lowered = result.lowered(); + let results_lowered = results + .iter() + .flat_map(|ty| ty.lowered()) + .collect::>(); let mut core_params = String::new(); let mut gets = String::new(); @@ -345,13 +356,13 @@ fn make_import_and_export(params: &[Type], result: &Type) -> String { format!("(param{core_params})") }; - if result_lowered.len() <= MAX_FLAT_RESULTS { + if results_lowered.len() <= MAX_FLAT_RESULTS { let mut core_results = String::new(); - for result in result_lowered.iter() { + for result in results_lowered.iter() { write!(&mut core_results, " {result}").unwrap(); } - let maybe_core_results = if result_lowered.is_empty() { + let maybe_core_results = if results_lowered.is_empty() { String::new() } else { format!("(result{core_results})") @@ -368,7 +379,8 @@ fn make_import_and_export(params: &[Type], result: &Type) -> String { )"# ) } else { - let SizeAndAlignment { size, alignment } = result.size_and_alignment(); + let SizeAndAlignment { size, alignment } = + Type::Record(VecInRange(results.to_vec())).size_and_alignment(); format!( r#" @@ -405,7 +417,6 @@ fn make_rust_name(name_counter: &mut u32) -> Ident { /// parameter is used to accumulate declarations for each recursively visited type. pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStream) -> TokenStream { match ty { - Type::Unit => quote!(()), Type::Bool => quote!(bool), Type::S8 => quote!(i8), Type::U8 => quote!(u8), @@ -458,29 +469,51 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre quote!((#fields)) } - Type::Variant(types) | Type::Union(types) => { + Type::Variant(types) => { let cases = types .0 .iter() .enumerate() .map(|(index, ty)| { let name = format_ident!("C{index}"); - let ty = rust_type(ty, name_counter, declarations); - quote!(#name(#ty),) + let ty = match ty { + Some(ty) => { + let ty = rust_type(ty, name_counter, declarations); + quote!((#ty)) + } + None => quote!(), + }; + quote!(#name #ty,) }) .collect::(); let name = make_rust_name(name_counter); + declarations.extend(quote! { + #[derive(ComponentType, Lift, Lower, PartialEq, Debug, Clone, Arbitrary)] + #[component(variant)] + enum #name { + #cases + } + }); - let which = if let Type::Variant(_) = ty { - quote!(variant) - } else { - quote!(union) - }; + quote!(#name) + } + Type::Union(types) => { + let cases = types + .0 + .iter() + .enumerate() + .map(|(index, ty)| { + let name = format_ident!("U{index}"); + let ty = rust_type(ty, name_counter, declarations); + quote!(#name(#ty),) + }) + .collect::(); + let name = make_rust_name(name_counter); declarations.extend(quote! { #[derive(ComponentType, Lift, Lower, PartialEq, Debug, Clone, Arbitrary)] - #[component(#which)] + #[component(union)] enum #name { #cases } @@ -491,7 +524,7 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre Type::Enum(count) => { let cases = (0..count.0) .map(|index| { - let name = format_ident!("C{index}"); + let name = format_ident!("E{index}"); quote!(#name,) }) .collect::(); @@ -512,9 +545,15 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre let ty = rust_type(ty, name_counter, declarations); quote!(Option<#ty>) } - Type::Expected { ok, err } => { - let ok = rust_type(ok, name_counter, declarations); - let err = rust_type(err, name_counter, declarations); + Type::Result { ok, err } => { + let ok = match ok { + Some(ok) => rust_type(ok, name_counter, declarations), + None => quote!(()), + }; + let err = match err { + Some(err) => rust_type(err, name_counter, declarations), + None => quote!(()), + }; quote!(Result<#ok, #err>) } Type::Flags(count) => { @@ -564,7 +603,6 @@ impl<'a> TypesBuilder<'a> { fn write_ref(&mut self, ty: &'a Type, dst: &mut String) { match ty { // Primitive types can be referenced directly - Type::Unit => dst.push_str("unit"), Type::Bool => dst.push_str("bool"), Type::S8 => dst.push_str("s8"), Type::U8 => dst.push_str("u8"), @@ -588,7 +626,7 @@ impl<'a> TypesBuilder<'a> { | Type::Enum(_) | Type::Union(_) | Type::Option(_) - | Type::Expected { .. } + | Type::Result { .. } | Type::Flags(_) => { let idx = self.next; self.next += 1; @@ -601,8 +639,7 @@ impl<'a> TypesBuilder<'a> { fn write_decl(&mut self, idx: u32, ty: &'a Type) -> String { let mut decl = format!("(type $t{idx} "); match ty { - Type::Unit - | Type::Bool + Type::Bool | Type::S8 | Type::U8 | Type::S16 @@ -641,8 +678,11 @@ impl<'a> TypesBuilder<'a> { Type::Variant(types) => { decl.push_str("(variant"); for (index, ty) in types.iter().enumerate() { - write!(decl, r#" (case "C{index}" "#).unwrap(); - self.write_ref(ty, &mut decl); + write!(decl, r#" (case "C{index}""#).unwrap(); + if let Some(ty) = ty { + decl.push_str(" "); + self.write_ref(ty, &mut decl); + } decl.push_str(")"); } decl.push_str(")"); @@ -667,11 +707,17 @@ impl<'a> TypesBuilder<'a> { self.write_ref(ty, &mut decl); decl.push_str(")"); } - Type::Expected { ok, err } => { - decl.push_str("(expected "); - self.write_ref(ok, &mut decl); - decl.push_str(" "); - self.write_ref(err, &mut decl); + Type::Result { ok, err } => { + decl.push_str("(result"); + if let Some(ok) = ok { + decl.push_str(" "); + self.write_ref(ok, &mut decl); + } + if let Some(err) = err { + decl.push_str(" (error "); + self.write_ref(err, &mut decl); + decl.push_str(")"); + } decl.push_str(")"); } Type::Flags(count) => { @@ -695,7 +741,7 @@ pub struct Declarations { /// Parameter declarations used for the imported and exported functions pub params: Cow<'static, str>, /// Result declaration used for the imported and exported functions - pub result: Cow<'static, str>, + pub results: Cow<'static, str>, /// A WAT fragment representing the core function import and export to use for testing pub import_and_export: Cow<'static, str>, /// String encoding to use for host -> component @@ -710,7 +756,7 @@ impl Declarations { let Self { types, params, - result, + results, import_and_export, encoding1, encoding2, @@ -768,7 +814,7 @@ impl Declarations { {types} - (type $sig (func {params} {result})) + (type $sig (func {params} {results})) (import "{IMPORT_FUNCTION}" (func $f (type $sig))) {c1} @@ -783,12 +829,12 @@ impl Declarations { } /// Represents a test case for calling a component function -#[derive(Debug)] +#[derive(Arbitrary, Debug)] pub struct TestCase { /// The types of parameters to pass to the function - pub params: Box<[Type]>, - /// The type of the result to be returned by the function - pub result: Type, + pub params: VecInRange, + /// The result types of the the function + pub results: VecInRange, /// String encoding to use from host-to-component. pub encoding1: StringEncoding, /// String encoding to use from component-to-host. @@ -801,17 +847,20 @@ impl TestCase { let mut builder = TypesBuilder::default(); let mut params = String::new(); - for ty in self.params.iter() { - params.push_str(" (param "); + for (i, ty) in self.params.iter().enumerate() { + params.push_str(&format!(" (param \"p{i}\" ")); builder.write_ref(ty, &mut params); params.push_str(")"); } - let mut result = String::from("(result "); - builder.write_ref(&self.result, &mut result); - result.push_str(")"); + let mut results = String::new(); + for (i, ty) in self.results.iter().enumerate() { + results.push_str(&format!(" (result \"r{i}\" ")); + builder.write_ref(ty, &mut results); + results.push_str(")"); + } - let import_and_export = make_import_and_export(&self.params, &self.result); + let import_and_export = make_import_and_export(&self.params, &self.results); let mut type_decls = Vec::new(); while let Some((idx, ty)) = builder.worklist.pop() { @@ -830,7 +879,7 @@ impl TestCase { Declarations { types: types.into(), params: params.into(), - result: result.into(), + results: results.into(), import_and_export: import_and_export.into(), encoding1: self.encoding1, encoding2: self.encoding2, @@ -838,21 +887,6 @@ impl TestCase { } } -impl<'a> Arbitrary<'a> for TestCase { - /// Generate an arbitrary [`TestCase`]. - fn arbitrary(input: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Self { - params: input - .arbitrary_iter()? - .take(MAX_ARITY) - .collect::>>()?, - result: input.arbitrary()?, - encoding1: input.arbitrary()?, - encoding2: input.arbitrary()?, - }) - } -} - #[derive(Copy, Clone, Debug, Arbitrary)] pub enum StringEncoding { Utf8, diff --git a/crates/misc/component-test-util/src/lib.rs b/crates/misc/component-test-util/src/lib.rs index c5a4970e6f88..6636dc7d50de 100644 --- a/crates/misc/component-test-util/src/lib.rs +++ b/crates/misc/component-test-util/src/lib.rs @@ -4,7 +4,7 @@ use std::mem::MaybeUninit; use wasmtime::component::__internal::{ CanonicalAbiInfo, ComponentTypes, InterfaceType, Memory, MemoryMut, Options, StoreOpaque, }; -use wasmtime::component::{ComponentParams, ComponentType, Func, Lift, Lower, TypedFunc, Val}; +use wasmtime::component::{ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, Val}; use wasmtime::{AsContextMut, Config, Engine, StoreContextMut}; pub trait TypedFuncExt { @@ -13,8 +13,8 @@ pub trait TypedFuncExt { impl TypedFuncExt for TypedFunc where - P: ComponentParams + Lower, - R: Lift, + P: ComponentNamedList + Lower, + R: ComponentNamedList + Lift, { fn call_and_post_return(&self, mut store: impl AsContextMut, params: P) -> Result { let result = self.call(&mut store, params)?; @@ -24,14 +24,24 @@ where } pub trait FuncExt { - fn call_and_post_return(&self, store: impl AsContextMut, args: &[Val]) -> Result; + fn call_and_post_return( + &self, + store: impl AsContextMut, + params: &[Val], + results: &mut [Val], + ) -> Result<()>; } impl FuncExt for Func { - fn call_and_post_return(&self, mut store: impl AsContextMut, args: &[Val]) -> Result { - let result = self.call(&mut store, args)?; + fn call_and_post_return( + &self, + mut store: impl AsContextMut, + params: &[Val], + results: &mut [Val], + ) -> Result<()> { + self.call(&mut store, params, results)?; self.post_return(&mut store)?; - Ok(result) + Ok(()) } } diff --git a/crates/wasmtime/src/component/func.rs b/crates/wasmtime/src/component/func.rs index ce00716377c9..f62df418a9f7 100644 --- a/crates/wasmtime/src/component/func.rs +++ b/crates/wasmtime/src/component/func.rs @@ -198,8 +198,8 @@ impl Func { /// # use wasmtime::component::Func; /// # use wasmtime::Store; /// # fn foo(func: &Func, mut store: Store<()>) -> anyhow::Result<()> { - /// let typed = func.typed::<(&str,), String, _>(&store)?; - /// let ret = typed.call(&mut store, ("Hello, ",))?; + /// let typed = func.typed::<(&str,), (String,), _>(&store)?; + /// let ret = typed.call(&mut store, ("Hello, ",))?.0; /// println!("returned string was: {}", ret); /// # Ok(()) /// # } @@ -211,16 +211,16 @@ impl Func { /// # use wasmtime::component::Func; /// # use wasmtime::Store; /// # fn foo(func: &Func, mut store: Store<()>) -> anyhow::Result<()> { - /// let typed = func.typed::<(u32, Option<&str>, &[u8]), bool, _>(&store)?; - /// let ok: bool = typed.call(&mut store, (1, Some("hello"), b"bytes!"))?; + /// let typed = func.typed::<(u32, Option<&str>, &[u8]), (bool,), _>(&store)?; + /// let ok: bool = typed.call(&mut store, (1, Some("hello"), b"bytes!"))?.0; /// println!("return value was: {ok}"); /// # Ok(()) /// # } /// ``` pub fn typed(&self, store: S) -> Result> where - Params: ComponentParams + Lower, - Return: Lift, + Params: ComponentNamedList + Lower, + Return: ComponentNamedList + Lift, S: AsContext, { self._typed(store.as_context().0) @@ -231,8 +231,8 @@ impl Func { store: &StoreOpaque, ) -> Result> where - Params: ComponentParams + Lower, - Return: Lift, + Params: ComponentNamedList + Lower, + Return: ComponentNamedList + Lift, { self.typecheck::(store)?; unsafe { Ok(TypedFunc::new_unchecked(*self)) } @@ -240,15 +240,16 @@ impl Func { fn typecheck(&self, store: &StoreOpaque) -> Result<()> where - Params: ComponentParams + Lower, - Return: Lift, + Params: ComponentNamedList + Lower, + Return: ComponentNamedList + Lift, { let data = &store[self.0]; let ty = &data.types[data.ty]; - Params::typecheck_params(&ty.params, &data.types) + Params::typecheck_named_list(&ty.params, &data.types) .context("type mismatch with parameters")?; - Return::typecheck(&ty.result, &data.types).context("type mismatch with result")?; + Return::typecheck_named_list(&ty.results, &data.types) + .context("type mismatch with results")?; Ok(()) } @@ -263,10 +264,14 @@ impl Func { .collect() } - /// Get the result type for this function. - pub fn result(&self, store: impl AsContext) -> Type { + /// Get the result types for this function. + pub fn results(&self, store: impl AsContext) -> Box<[Type]> { let data = &store.as_context()[self.0]; - Type::from(&data.types[data.ty].result, &data.types) + data.types[data.ty] + .results + .iter() + .map(|(_, ty)| Type::from(ty, &data.types)) + .collect() } /// Invokes this function with the `params` given and returns the result. @@ -274,64 +279,71 @@ impl Func { /// The `params` here must match the type signature of this `Func`, or this will return an error. If a trap /// occurs while executing this function, then an error will also be returned. // TODO: say more -- most of the docs for `TypedFunc::call` apply here, too - pub fn call(&self, mut store: impl AsContextMut, args: &[Val]) -> Result { + pub fn call( + &self, + mut store: impl AsContextMut, + params: &[Val], + results: &mut [Val], + ) -> Result<()> { let store = &mut store.as_context_mut(); - let params; - let result; + let param_tys = self.params(&store); + let result_tys = self.results(&store); - { - let data = &store[self.0]; - let ty = &data.types[data.ty]; - - if ty.params.len() != args.len() { - bail!( - "expected {} argument(s), got {}", - ty.params.len(), - args.len() - ); - } - - params = ty - .params - .iter() - .zip(args) - .map(|((_, ty), arg)| { - let ty = Type::from(ty, &data.types); - - ty.check(arg).context("type mismatch with parameters")?; - - Ok(ty) - }) - .collect::>>()?; + if param_tys.len() != params.len() { + bail!( + "expected {} argument(s), got {}", + param_tys.len(), + params.len() + ); + } + if result_tys.len() != results.len() { + bail!( + "expected {} results(s), got {}", + result_tys.len(), + results.len() + ); + } - result = Type::from(&ty.result, &data.types); + for (param, ty) in params.iter().zip(param_tys.iter()) { + ty.check(param).context("type mismatch with parameters")?; } - let param_abi = CanonicalAbiInfo::record(params.iter().map(|t| t.canonical_abi())); - let result_count = result.canonical_abi().flat_count(MAX_FLAT_RESULTS); + let param_abi = CanonicalAbiInfo::record(param_tys.iter().map(|t| t.canonical_abi())); + let result_abi = CanonicalAbiInfo::record(result_tys.iter().map(|t| t.canonical_abi())); self.call_raw( store, - args, - |store, options, args, dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>| { - if param_abi.flat_count(MAX_FLAT_PARAMS).is_none() { - self.store_args(store, &options, ¶m_abi, ¶ms, args, dst) - } else { + params, + |store, options, params, dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>| { + if param_abi.flat_count(MAX_FLAT_PARAMS).is_some() { let dst = &mut unsafe { mem::transmute::<_, &mut [MaybeUninit; MAX_FLAT_PARAMS]>(dst) } .iter_mut(); - args.iter() - .try_for_each(|arg| arg.lower(store, &options, dst)) + params + .iter() + .try_for_each(|param| param.lower(store, &options, dst)) + } else { + self.store_args(store, &options, ¶m_abi, ¶m_tys, params, dst) } }, |store, options, src: &[ValRaw; MAX_FLAT_RESULTS]| { - if result_count.is_none() { - Self::load_result(&Memory::new(store, &options), &result, &mut src.iter()) + if result_abi.flat_count(MAX_FLAT_RESULTS).is_some() { + let mut flat = src.iter(); + for (ty, slot) in result_tys.iter().zip(results) { + *slot = Val::lift(ty, store, &options, &mut flat)?; + } + Ok(()) } else { - Val::lift(&result, store, &options, &mut src.iter()) + Self::load_results( + &Memory::new(store, &options), + &result_abi, + &result_tys, + results, + &mut src.iter(), + ) } }, ) @@ -567,12 +579,13 @@ impl Func { Ok(()) } - fn load_result<'a>( + fn load_results<'a>( mem: &Memory, - ty: &Type, + abi: &CanonicalAbiInfo, + result_tys: &[Type], + results: &mut [Val], src: &mut std::slice::Iter<'_, ValRaw>, - ) -> Result { - let abi = ty.canonical_abi(); + ) -> Result<()> { // FIXME: needs to read an i64 for memory64 let ptr = usize::try_from(src.next().unwrap().get_u32())?; if ptr % usize::try_from(abi.align32)? != 0 { @@ -585,6 +598,12 @@ impl Func { .and_then(|b| b.get(..usize::try_from(abi.size32).unwrap())) .ok_or_else(|| anyhow::anyhow!("pointer out of bounds of memory"))?; - Val::load(ty, mem, bytes) + let mut offset = 0; + for (ty, slot) in result_tys.iter().zip(results) { + let abi = ty.canonical_abi(); + let offset = abi.next_field32_size(&mut offset); + *slot = Val::load(ty, mem, &bytes[offset..][..abi.size32 as usize])?; + } + Ok(()) } } diff --git a/crates/wasmtime/src/component/func/host.rs b/crates/wasmtime/src/component/func/host.rs index b7301e2698fd..e3c5c520e31e 100644 --- a/crates/wasmtime/src/component/func/host.rs +++ b/crates/wasmtime/src/component/func/host.rs @@ -1,6 +1,6 @@ use crate::component::func::{Memory, MemoryMut, Options}; use crate::component::storage::slice_to_storage_mut; -use crate::component::{ComponentParams, ComponentType, Lift, Lower, Type, Val}; +use crate::component::{ComponentNamedList, ComponentType, Lift, Lower, Type, Val}; use crate::{AsContextMut, StoreContextMut, ValRaw}; use anyhow::{anyhow, bail, Context, Result}; use std::any::Any; @@ -53,8 +53,8 @@ impl HostFunc { fn new(func: F, entrypoint: VMLoweringCallee) -> Arc where F: Send + Sync + 'static, - P: ComponentParams + Lift + 'static, - R: Lower + 'static, + P: ComponentNamedList + Lift + 'static, + R: ComponentNamedList + Lower + 'static, { Arc::new(HostFunc { entrypoint, @@ -63,14 +63,14 @@ impl HostFunc { }) } - pub(crate) fn new_dynamic< - T, - F: Fn(StoreContextMut<'_, T>, &[Val]) -> Result + Send + Sync + 'static, - >( + pub(crate) fn new_dynamic( func: F, index: TypeFuncIndex, types: &Arc, - ) -> Arc { + ) -> Arc + where + F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, + { let ty = &types[index]; Arc::new(HostFunc { @@ -94,7 +94,11 @@ impl HostFunc { .iter() .map(|(_, ty)| Type::from(ty, types)) .collect(), - result: Type::from(&ty.result, types), + results: ty + .results + .iter() + .map(|(_, ty)| Type::from(ty, types)) + .collect(), }, }), }) @@ -115,12 +119,12 @@ impl HostFunc { fn typecheck(ty: TypeFuncIndex, types: &Arc) -> Result<()> where - P: ComponentParams + Lift, - R: Lower, + P: ComponentNamedList + Lift, + R: ComponentNamedList + Lower, { let ty = &types[ty]; - P::typecheck_params(&ty.params, types).context("type mismatch with parameters")?; - R::typecheck(&ty.result, types).context("type mismatch with result")?; + P::typecheck_named_list(&ty.params, types).context("type mismatch with parameters")?; + R::typecheck_named_list(&ty.results, types).context("type mismatch with results")?; Ok(()) } @@ -281,8 +285,8 @@ macro_rules! impl_into_component_func { impl IntoComponentFunc for F where F: Fn($($args),*) -> Result + Send + Sync + 'static, - ($($args,)*): ComponentParams + Lift + 'static, - R: Lower + 'static, + ($($args,)*): ComponentNamedList + Lift + 'static, + R: ComponentNamedList + Lower + 'static, { extern "C" fn entrypoint( cx: *mut VMOpaqueContext, @@ -319,8 +323,8 @@ macro_rules! impl_into_component_func { impl IntoComponentFunc, $($args,)*), R> for F where F: Fn(StoreContextMut<'_, T>, $($args),*) -> Result + Send + Sync + 'static, - ($($args,)*): ComponentParams + Lift + 'static, - R: Lower + 'static, + ($($args,)*): ComponentNamedList + Lift + 'static, + R: ComponentNamedList + Lower + 'static, { extern "C" fn entrypoint( cx: *mut VMOpaqueContext, @@ -357,7 +361,7 @@ macro_rules! impl_into_component_func { for_each_function_signature!(impl_into_component_func); unsafe fn call_host_dynamic( - Types { params, result }: &Types, + Types { params, results }: &Types, cx: *mut VMOpaqueContext, mut flags: InstanceFlags, memory: *mut VMMemoryDefinition, @@ -367,7 +371,7 @@ unsafe fn call_host_dynamic( closure: F, ) -> Result<()> where - F: FnOnce(StoreContextMut<'_, T>, &[Val]) -> Result, + F: FnOnce(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()>, { let cx = VMComponentContext::from_opaque(cx); let instance = (*cx).instance(); @@ -391,15 +395,7 @@ where let ret_index; let param_abi = CanonicalAbiInfo::record(params.iter().map(|t| t.canonical_abi())); - let param_count = param_abi.flat_count.and_then(|i| { - let i = usize::from(i); - if i > MAX_FLAT_PARAMS { - None - } else { - Some(i) - } - }); - if let Some(param_count) = param_count { + if let Some(param_count) = param_abi.flat_count(MAX_FLAT_PARAMS) { let iter = &mut storage.iter(); args = params .iter() @@ -424,20 +420,31 @@ where ret_index = 1; }; - let ret = closure(cx.as_context_mut(), &args)?; + let mut result_vals = Vec::with_capacity(results.len()); + for _ in results.iter() { + result_vals.push(Val::Bool(false)); + } + closure(cx.as_context_mut(), &args, &mut result_vals)?; flags.set_may_leave(false); - result.check(&ret)?; + for (val, ty) in result_vals.iter().zip(results.iter()) { + ty.check(val)?; + } - let result_count = result.canonical_abi().flat_count(MAX_FLAT_RESULTS); - if result_count.is_some() { + let result_abi = CanonicalAbiInfo::record(results.iter().map(|t| t.canonical_abi())); + if result_abi.flat_count(MAX_FLAT_RESULTS).is_some() { let dst = mem::transmute::<&mut [ValRaw], &mut [MaybeUninit]>(storage); - ret.lower(&mut cx, &options, &mut dst.iter_mut())?; + let mut dst = dst.iter_mut(); + for val in result_vals.iter() { + val.lower(&mut cx, &options, &mut dst)?; + } } else { let ret_ptr = &storage[ret_index]; let mut memory = MemoryMut::new(cx.as_context_mut(), &options); - let ptr = - validate_inbounds_dynamic(result.canonical_abi(), memory.as_slice_mut(), ret_ptr)?; - ret.store(&mut memory, ptr)?; + let mut ptr = validate_inbounds_dynamic(&result_abi, memory.as_slice_mut(), ret_ptr)?; + for (val, ty) in result_vals.iter().zip(results.iter()) { + let offset = ty.canonical_abi().next_field32_size(&mut ptr); + val.store(&mut memory, offset)?; + } } flags.set_may_leave(true); @@ -463,7 +470,7 @@ fn validate_inbounds_dynamic(abi: &CanonicalAbiInfo, memory: &[u8], ptr: &ValRaw struct Types { params: Box<[Type]>, - result: Type, + results: Box<[Type]>, } struct DynamicContext { @@ -471,10 +478,7 @@ struct DynamicContext { types: Types, } -extern "C" fn dynamic_entrypoint< - T, - F: Fn(StoreContextMut<'_, T>, &[Val]) -> Result + Send + Sync + 'static, ->( +extern "C" fn dynamic_entrypoint( cx: *mut VMOpaqueContext, data: *mut u8, flags: InstanceFlags, @@ -483,7 +487,9 @@ extern "C" fn dynamic_entrypoint< string_encoding: StringEncoding, storage: *mut ValRaw, storage_len: usize, -) { +) where + F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, +{ let data = data as *const DynamicContext; unsafe { handle_result(|| { @@ -495,7 +501,7 @@ extern "C" fn dynamic_entrypoint< realloc, string_encoding, std::slice::from_raw_parts_mut(storage, storage_len), - |store, values| ((*data).func)(store, values), + |store, params, results| ((*data).func)(store, params, results), ) }) } diff --git a/crates/wasmtime/src/component/func/typed.rs b/crates/wasmtime/src/component/func/typed.rs index ae7019e881e2..bc88fb0ab26a 100644 --- a/crates/wasmtime/src/component/func/typed.rs +++ b/crates/wasmtime/src/component/func/typed.rs @@ -60,7 +60,7 @@ impl Clone for TypedFunc { impl TypedFunc where - Params: ComponentParams + Lower, + Params: ComponentNamedList + Lower, Return: Lift, { /// Creates a new [`TypedFunc`] from the provided component [`Func`], @@ -288,8 +288,8 @@ where } } -/// A trait representing a static list of parameters that can be passed to a -/// [`TypedFunc`]. +/// A trait representing a static list of named types that can be passed to or +/// returned from a [`TypedFunc`]. /// /// This trait is implemented for a number of tuple types and is not expected /// to be implemented externally. The contents of this trait are hidden as it's @@ -304,11 +304,11 @@ where // would not be memory safe. The main reason this is `unsafe` is the // `typecheck` function which must operate correctly relative to the `AsTuple` // interpretation of the implementor. -pub unsafe trait ComponentParams: ComponentType { - /// Performs a typecheck to ensure that this `ComponentParams` implementor - /// matches the types of the types in `params`. +pub unsafe trait ComponentNamedList: ComponentType { + /// Performs a typecheck to ensure that this `ComponentNamedList` + /// implementor matches the types of the types in `params`. #[doc(hidden)] - fn typecheck_params( + fn typecheck_named_list( params: &[(Option, InterfaceType)], types: &ComponentTypes, ) -> Result<()>; @@ -374,6 +374,9 @@ pub unsafe trait ComponentType { #[doc(hidden)] const ALIGN32: u32 = Self::ABI.align32; + #[doc(hidden)] + const IS_RUST_UNIT_TYPE: bool = false; + /// Returns the number of core wasm abi values will be used to represent /// this type in its lowered form. /// @@ -393,7 +396,7 @@ pub unsafe trait ComponentType { #[doc(hidden)] pub unsafe trait ComponentVariant: ComponentType { - const CASES: &'static [CanonicalAbiInfo]; + const CASES: &'static [Option]; const INFO: VariantInfo = VariantInfo::new_static(Self::CASES); const PAYLOAD_OFFSET32: usize = Self::INFO.payload_offset32 as usize; } @@ -1352,16 +1355,9 @@ fn typecheck_tuple( expected: &[fn(&InterfaceType, &ComponentTypes) -> Result<()>], ) -> Result<()> { match ty { - InterfaceType::Unit if expected.len() == 0 => Ok(()), InterfaceType::Tuple(t) => { let tuple = &types[*t]; if tuple.types.len() != expected.len() { - if expected.len() == 0 { - bail!( - "expected unit or 0-tuple, found {}-tuple", - tuple.types.len(), - ); - } bail!( "expected {}-tuple, found {}-tuple", expected.len(), @@ -1373,9 +1369,6 @@ fn typecheck_tuple( } Ok(()) } - other if expected.len() == 0 => { - bail!("expected `unit` or 0-tuple found `{}`", desc(other)) - } other => bail!("expected `tuple` found `{}`", desc(other)), } } @@ -1419,7 +1412,10 @@ pub fn typecheck_record( pub fn typecheck_variant( ty: &InterfaceType, types: &ComponentTypes, - expected: &[(&str, fn(&InterfaceType, &ComponentTypes) -> Result<()>)], + expected: &[( + &str, + Option Result<()>>, + )], ) -> Result<()> { match ty { InterfaceType::Variant(index) => { @@ -1434,11 +1430,20 @@ pub fn typecheck_variant( } for (case, &(name, check)) in cases.iter().zip(expected) { - check(&case.ty, types) - .with_context(|| format!("type mismatch for case {}", name))?; - if case.name != name { - bail!("expected variant case named {}, found {}", name, case.name); + bail!("expected variant case named {name}, found {}", case.name); + } + + match (check, &case.ty) { + (Some(check), Some(ty)) => check(ty, types) + .with_context(|| format!("type mismatch for case {name}"))?, + (None, None) => {} + (Some(_), None) => { + bail!("case `{name}` has no type but one was expected") + } + (None, Some(_)) => { + bail!("case `{name}` has a type but none was expected") + } } } @@ -1558,8 +1563,7 @@ where { type Lower = TupleLower2<::Lower, T::Lower>; - const ABI: CanonicalAbiInfo = - CanonicalAbiInfo::variant_static(&[<() as ComponentType>::ABI, T::ABI]); + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[None, Some(T::ABI)]); fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> { match ty { @@ -1573,7 +1577,7 @@ unsafe impl ComponentVariant for Option where T: ComponentType, { - const CASES: &'static [CanonicalAbiInfo] = &[<() as ComponentType>::ABI, T::ABI]; + const CASES: &'static [Option] = &[None, Some(T::ABI)]; } unsafe impl Lower for Option @@ -1667,17 +1671,25 @@ where { type Lower = ResultLower; - const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[T::ABI, E::ABI]); + const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[Some(T::ABI), Some(E::ABI)]); fn typecheck(ty: &InterfaceType, types: &ComponentTypes) -> Result<()> { match ty { - InterfaceType::Expected(r) => { - let expected = &types[*r]; - T::typecheck(&expected.ok, types)?; - E::typecheck(&expected.err, types)?; + InterfaceType::Result(r) => { + let result = &types[*r]; + match &result.ok { + Some(ty) => T::typecheck(ty, types)?, + None if T::IS_RUST_UNIT_TYPE => {} + None => bail!("expected no `ok` type"), + } + match &result.err { + Some(ty) => E::typecheck(ty, types)?, + None if E::IS_RUST_UNIT_TYPE => {} + None => bail!("expected no `err` type"), + } Ok(()) } - other => bail!("expected `expected` found `{}`", desc(other)), + other => bail!("expected `result` found `{}`", desc(other)), } } } @@ -1715,7 +1727,7 @@ where T: ComponentType, E: ComponentType, { - const CASES: &'static [CanonicalAbiInfo] = &[T::ABI, E::ABI]; + const CASES: &'static [Option] = &[Some(T::ABI), Some(E::ABI)]; } unsafe impl Lower for Result @@ -1896,6 +1908,15 @@ macro_rules! impl_component_ty_for_tuples { $($t::ABI),* ]); + const IS_RUST_UNIT_TYPE: bool = { + let mut _is_unit = true; + $( + let _anything_to_bind_the_macro_variable = $t::IS_RUST_UNIT_TYPE; + _is_unit = false; + )* + _is_unit + }; + fn typecheck( ty: &InterfaceType, types: &ComponentTypes, @@ -1944,19 +1965,19 @@ macro_rules! impl_component_ty_for_tuples { } #[allow(non_snake_case)] - unsafe impl<$($t,)*> ComponentParams for ($($t,)*) + unsafe impl<$($t,)*> ComponentNamedList for ($($t,)*) where $($t: ComponentType),* { - fn typecheck_params( - params: &[(Option, InterfaceType)], + fn typecheck_named_list( + names: &[(Option, InterfaceType)], _types: &ComponentTypes, ) -> Result<()> { - if params.len() != $n { - bail!("expected {} types, found {}", $n, params.len()); + if names.len() != $n { + bail!("expected {} types, found {}", $n, names.len()); } - let mut params = params.iter().map(|i| &i.1); - $($t::typecheck(params.next().unwrap(), _types)?;)* - debug_assert!(params.next().is_none()); + let mut names = names.iter().map(|i| &i.1); + $($t::typecheck(names.next().unwrap(), _types)?;)* + debug_assert!(names.next().is_none()); Ok(()) } } @@ -1978,14 +1999,13 @@ fn desc(ty: &InterfaceType) -> &'static str { InterfaceType::S64 => "s64", InterfaceType::Float32 => "f32", InterfaceType::Float64 => "f64", - InterfaceType::Unit => "unit", InterfaceType::Bool => "bool", InterfaceType::Char => "char", InterfaceType::String => "string", InterfaceType::List(_) => "list", InterfaceType::Tuple(_) => "tuple", InterfaceType::Option(_) => "option", - InterfaceType::Expected(_) => "expected", + InterfaceType::Result(_) => "result", InterfaceType::Record(_) => "record", InterfaceType::Variant(_) => "variant", diff --git a/crates/wasmtime/src/component/instance.rs b/crates/wasmtime/src/component/instance.rs index 7abc81bfeafa..0fcb62fbe619 100644 --- a/crates/wasmtime/src/component/instance.rs +++ b/crates/wasmtime/src/component/instance.rs @@ -1,5 +1,5 @@ use crate::component::func::HostFunc; -use crate::component::{Component, ComponentParams, Func, Lift, Lower, TypedFunc}; +use crate::component::{Component, ComponentNamedList, Func, Lift, Lower, TypedFunc}; use crate::instance::OwnedImports; use crate::store::{StoreOpaque, Stored}; use crate::{AsContextMut, Module, StoreContextMut}; @@ -90,8 +90,8 @@ impl Instance { name: &str, ) -> Result> where - Params: ComponentParams + Lower, - Results: Lift, + Params: ComponentNamedList + Lower, + Results: ComponentNamedList + Lift, S: AsContextMut, { let f = self @@ -585,8 +585,8 @@ impl<'a, 'store> ExportInstance<'a, 'store> { /// Same as [`Instance::get_typed_func`] pub fn typed_func(&mut self, name: &str) -> Result> where - Params: ComponentParams + Lower, - Results: Lift, + Params: ComponentNamedList + Lower, + Results: ComponentNamedList + Lift, { let func = self .func(name) diff --git a/crates/wasmtime/src/component/linker.rs b/crates/wasmtime/src/component/linker.rs index 1289deb632ac..8978abb0d408 100644 --- a/crates/wasmtime/src/component/linker.rs +++ b/crates/wasmtime/src/component/linker.rs @@ -238,7 +238,7 @@ impl LinkerInstance<'_, T> { /// called, which must return a `Val` which is an instance of the result /// type of the import. pub fn func_new< - F: Fn(StoreContextMut<'_, T>, &[Val]) -> Result + Send + Sync + 'static, + F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, >( &mut self, component: &Component, diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index bb9f3923b6e2..1b49ca0e3ef6 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -14,7 +14,7 @@ pub mod types; mod values; pub use self::component::Component; pub use self::func::{ - ComponentParams, ComponentType, Func, IntoComponentFunc, Lift, Lower, TypedFunc, WasmList, + ComponentNamedList, ComponentType, Func, IntoComponentFunc, Lift, Lower, TypedFunc, WasmList, WasmStr, }; pub use self::instance::{ExportInstance, Exports, Instance, InstancePre}; diff --git a/crates/wasmtime/src/component/types.rs b/crates/wasmtime/src/component/types.rs index 1b30135ae5ad..a1dacacbc87e 100644 --- a/crates/wasmtime/src/component/types.rs +++ b/crates/wasmtime/src/component/types.rs @@ -7,8 +7,8 @@ use std::mem; use std::ops::Deref; use std::sync::Arc; use wasmtime_environ::component::{ - CanonicalAbiInfo, ComponentTypes, InterfaceType, TypeEnumIndex, TypeExpectedIndex, - TypeFlagsIndex, TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, TypeTupleIndex, + CanonicalAbiInfo, ComponentTypes, InterfaceType, TypeEnumIndex, TypeFlagsIndex, + TypeInterfaceIndex, TypeOptionIndex, TypeRecordIndex, TypeResultIndex, TypeTupleIndex, TypeUnionIndex, TypeVariantIndex, VariantInfo, }; @@ -111,8 +111,8 @@ impl Tuple { pub struct Case<'a> { /// The name of the case pub name: &'a str, - /// The type of the case - pub ty: Type, + /// The optional payload type of the case + pub ty: Option, } /// A `variant` interface type @@ -121,7 +121,7 @@ pub struct Variant(Handle); impl Variant { /// Instantiate this type with the specified case `name` and `value`. - pub fn new_val(&self, name: &str, value: Val) -> Result { + pub fn new_val(&self, name: &str, value: Option) -> Result { Ok(Val::Variant(values::Variant::new(self, name, value)?)) } @@ -129,7 +129,7 @@ impl Variant { pub fn cases(&self) -> impl ExactSizeIterator { self.0.types[self.0.index].cases.iter().map(|case| Case { name: &case.name, - ty: Type::from(&case.ty, &self.0.types), + ty: case.ty.as_ref().map(|ty| Type::from(ty, &self.0.types)), }) } @@ -198,12 +198,12 @@ impl Union { /// An `option` interface type #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Option(Handle); +pub struct OptionType(Handle); -impl Option { +impl OptionType { /// Instantiate this type with the specified `value`. - pub fn new_val(&self, value: std::option::Option) -> Result { - Ok(Val::Option(values::Option::new(self, value)?)) + pub fn new_val(&self, value: Option) -> Result { + Ok(Val::Option(values::OptionVal::new(self, value)?)) } /// Retrieve the type parameter for this `option`. @@ -222,22 +222,28 @@ impl Option { /// An `expected` interface type #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Expected(Handle); +pub struct ResultType(Handle); -impl Expected { +impl ResultType { /// Instantiate this type with the specified `value`. - pub fn new_val(&self, value: Result) -> Result { - Ok(Val::Expected(values::Expected::new(self, value)?)) + pub fn new_val(&self, value: Result, Option>) -> Result { + Ok(Val::Result(values::ResultVal::new(self, value)?)) } /// Retrieve the `ok` type parameter for this `option`. - pub fn ok(&self) -> Type { - Type::from(&self.0.types[self.0.index].ok, &self.0.types) + pub fn ok(&self) -> Option { + Some(Type::from( + self.0.types[self.0.index].ok.as_ref()?, + &self.0.types, + )) } /// Retrieve the `err` type parameter for this `option`. - pub fn err(&self) -> Type { - Type::from(&self.0.types[self.0.index].err, &self.0.types) + pub fn err(&self) -> Option { + Some(Type::from( + self.0.types[self.0.index].err.as_ref()?, + &self.0.types, + )) } pub(crate) fn variant_info(&self) -> &VariantInfo { @@ -274,52 +280,29 @@ impl Flags { /// Represents a component model interface type #[derive(Clone, PartialEq, Eq, Debug)] +#[allow(missing_docs)] pub enum Type { - /// Unit - Unit, - /// Boolean Bool, - /// Signed 8-bit integer S8, - /// Unsigned 8-bit integer U8, - /// Signed 16-bit integer S16, - /// Unsigned 16-bit integer U16, - /// Signed 32-bit integer S32, - /// Unsigned 32-bit integer U32, - /// Signed 64-bit integer S64, - /// Unsigned 64-bit integer U64, - /// 64-bit floating point value Float32, - /// 64-bit floating point value Float64, - /// 32-bit character Char, - /// Character string String, - /// List of values List(List), - /// Record Record(Record), - /// Tuple Tuple(Tuple), - /// Variant Variant(Variant), - /// Enum Enum(Enum), - /// Union Union(Union), - /// Option - Option(Option), - /// Expected - Expected(Expected), - /// Bit flags + Option(OptionType), + Result(ResultType), Flags(Flags), } @@ -402,12 +385,12 @@ impl Type { } } - /// Retrieve the inner [`Option`] of a [`Type::Option`]. + /// Retrieve the inner [`OptionType`] of a [`Type::Option`]. /// /// # Panics /// /// This will panic if `self` is not a [`Type::Option`]. - pub fn unwrap_option(&self) -> &Option { + pub fn unwrap_option(&self) -> &OptionType { if let Type::Option(handle) = self { &handle } else { @@ -415,16 +398,16 @@ impl Type { } } - /// Retrieve the inner [`Expected`] of a [`Type::Expected`]. + /// Retrieve the inner [`ResultType`] of a [`Type::Result`]. /// /// # Panics /// - /// This will panic if `self` is not a [`Type::Expected`]. - pub fn unwrap_expected(&self) -> &Expected { - if let Type::Expected(handle) = self { + /// This will panic if `self` is not a [`Type::Result`]. + pub fn unwrap_result(&self) -> &ResultType { + if let Type::Result(handle) = self { &handle } else { - panic!("attempted to unwrap a {} as a expected", self.desc()) + panic!("attempted to unwrap a {} as a result", self.desc()) } } @@ -462,7 +445,6 @@ impl Type { /// Convert the specified `InterfaceType` to a `Type`. pub(crate) fn from(ty: &InterfaceType, types: &Arc) -> Self { match ty { - InterfaceType::Unit => Type::Unit, InterfaceType::Bool => Type::Bool, InterfaceType::S8 => Type::S8, InterfaceType::U8 => Type::U8, @@ -500,11 +482,11 @@ impl Type { index: *index, types: types.clone(), })), - InterfaceType::Option(index) => Type::Option(Option(Handle { + InterfaceType::Option(index) => Type::Option(OptionType(Handle { index: *index, types: types.clone(), })), - InterfaceType::Expected(index) => Type::Expected(Expected(Handle { + InterfaceType::Result(index) => Type::Result(ResultType(Handle { index: *index, types: types.clone(), })), @@ -517,7 +499,6 @@ impl Type { fn desc(&self) -> &'static str { match self { - Type::Unit => "unit", Type::Bool => "bool", Type::S8 => "s8", Type::U8 => "u8", @@ -538,7 +519,7 @@ impl Type { Type::Enum(_) => "enum", Type::Union(_) => "union", Type::Option(_) => "option", - Type::Expected(_) => "expected", + Type::Result(_) => "result", Type::Flags(_) => "flags", } } @@ -546,7 +527,6 @@ impl Type { /// Calculate the size and alignment requirements for the specified type. pub(crate) fn canonical_abi(&self) -> &CanonicalAbiInfo { match self { - Type::Unit => &CanonicalAbiInfo::ZERO, Type::Bool | Type::S8 | Type::U8 => &CanonicalAbiInfo::SCALAR1, Type::S16 | Type::U16 => &CanonicalAbiInfo::SCALAR2, Type::S32 | Type::U32 | Type::Char | Type::Float32 => &CanonicalAbiInfo::SCALAR4, @@ -558,7 +538,7 @@ impl Type { Type::Enum(handle) => handle.canonical_abi(), Type::Union(handle) => handle.canonical_abi(), Type::Option(handle) => handle.canonical_abi(), - Type::Expected(handle) => handle.canonical_abi(), + Type::Result(handle) => handle.canonical_abi(), Type::Flags(handle) => handle.canonical_abi(), } } diff --git a/crates/wasmtime/src/component/values.rs b/crates/wasmtime/src/component/values.rs index adfe6bc53d97..9f87ba1a5c06 100644 --- a/crates/wasmtime/src/component/values.rs +++ b/crates/wasmtime/src/component/values.rs @@ -180,12 +180,12 @@ impl fmt::Debug for Tuple { pub struct Variant { ty: types::Variant, discriminant: u32, - value: Box, + value: Option>, } impl Variant { /// Instantiate the specified type with the specified case `name` and `value`. - pub fn new(ty: &types::Variant, name: &str, value: Val) -> Result { + pub fn new(ty: &types::Variant, name: &str, value: Option) -> Result { let (discriminant, case_type) = ty .cases() .enumerate() @@ -198,14 +198,12 @@ impl Variant { }) .ok_or_else(|| anyhow!("unknown variant case: {name}"))?; - case_type - .check(&value) - .with_context(|| format!("type mismatch for case {name} of variant"))?; + typecheck_payload(name, case_type.as_ref(), value.as_ref())?; Ok(Self { ty: ty.clone(), discriminant: u32::try_from(discriminant)?, - value: Box::new(value), + value: value.map(Box::new), }) } @@ -224,15 +222,26 @@ impl Variant { } /// Returns the payload value for this variant. - pub fn payload(&self) -> &Val { - &self.value + pub fn payload(&self) -> Option<&Val> { + self.value.as_deref() + } +} + +fn typecheck_payload(name: &str, case_type: Option<&Type>, value: Option<&Val>) -> Result<()> { + match (case_type, value) { + (Some(expected), Some(actual)) => expected + .check(&actual) + .with_context(|| format!("type mismatch for case {name} of variant")), + (None, None) => Ok(()), + (Some(_), None) => bail!("expected a payload for case `{name}`"), + (None, Some(_)) => bail!("did not expect payload for case `{name}`"), } } impl fmt::Debug for Variant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(self.discriminant()) - .field(self.payload()) + .field(&self.payload()) .finish() } } @@ -279,7 +288,7 @@ impl fmt::Debug for Enum { pub struct Union { ty: types::Union, discriminant: u32, - value: Box, + value: Option>, } impl Union { @@ -293,7 +302,7 @@ impl Union { Ok(Self { ty: ty.clone(), discriminant, - value: Box::new(value), + value: Some(Box::new(value)), }) } else { Err(anyhow!( @@ -315,7 +324,7 @@ impl Union { /// Returns the payload value for this union. pub fn payload(&self) -> &Val { - &self.value + self.value.as_ref().unwrap() } } @@ -328,15 +337,15 @@ impl fmt::Debug for Union { } #[derive(PartialEq, Eq, Clone)] -pub struct Option { - ty: types::Option, +pub struct OptionVal { + ty: types::OptionType, discriminant: u32, - value: Box, + value: Option>, } -impl Option { +impl OptionVal { /// Instantiate the specified type with the specified `value`. - pub fn new(ty: &types::Option, value: std::option::Option) -> Result { + pub fn new(ty: &types::OptionType, value: Option) -> Result { let value = value .map(|value| { ty.ty().check(&value).context("type mismatch for option")?; @@ -348,77 +357,69 @@ impl Option { Ok(Self { ty: ty.clone(), discriminant: if value.is_none() { 0 } else { 1 }, - value: Box::new(value.unwrap_or(Val::Unit)), + value: value.map(Box::new), }) } /// Returns the type of this value. - pub fn ty(&self) -> &types::Option { + pub fn ty(&self) -> &types::OptionType { &self.ty } /// Returns the optional value contained within. - pub fn value(&self) -> std::option::Option<&Val> { - if self.discriminant == 0 { - None - } else { - Some(&self.value) - } + pub fn value(&self) -> Option<&Val> { + self.value.as_deref() } } -impl fmt::Debug for Option { +impl fmt::Debug for OptionVal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.value().fmt(f) } } #[derive(PartialEq, Eq, Clone)] -pub struct Expected { - ty: types::Expected, +pub struct ResultVal { + ty: types::ResultType, discriminant: u32, - value: Box, + value: Option>, } -impl Expected { +impl ResultVal { /// Instantiate the specified type with the specified `value`. - pub fn new(ty: &types::Expected, value: Result) -> Result { + pub fn new(ty: &types::ResultType, value: Result, Option>) -> Result { Ok(Self { ty: ty.clone(), discriminant: if value.is_ok() { 0 } else { 1 }, - value: Box::new(match value { + value: match value { Ok(value) => { - ty.ok() - .check(&value) - .context("type mismatch for ok case of expected")?; - value + typecheck_payload("ok", ty.ok().as_ref(), value.as_ref())?; + value.map(Box::new) } Err(value) => { - ty.err() - .check(&value) - .context("type mismatch for err case of expected")?; - value + typecheck_payload("err", ty.err().as_ref(), value.as_ref())?; + value.map(Box::new) } - }), + }, }) } /// Returns the type of this value. - pub fn ty(&self) -> &types::Expected { + pub fn ty(&self) -> &types::ResultType { &self.ty } /// Returns the result value contained within. - pub fn value(&self) -> Result<&Val, &Val> { + pub fn value(&self) -> Result, Option<&Val>> { if self.discriminant == 0 { - Ok(&self.value) + Ok(self.value.as_deref()) } else { - Err(&self.value) + Err(self.value.as_deref()) } } } -impl fmt::Debug for Expected { +impl fmt::Debug for ResultVal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.value().fmt(f) } @@ -487,52 +488,29 @@ impl fmt::Debug for Flags { /// Represents possible runtime values which a component function can either consume or produce #[derive(Debug, PartialEq, Eq, Clone)] +#[allow(missing_docs)] pub enum Val { - /// Unit - Unit, - /// Boolean Bool(bool), - /// Signed 8-bit integer S8(i8), - /// Unsigned 8-bit integer U8(u8), - /// Signed 16-bit integer S16(i16), - /// Unsigned 16-bit integer U16(u16), - /// Signed 32-bit integer S32(i32), - /// Unsigned 32-bit integer U32(u32), - /// Signed 64-bit integer S64(i64), - /// Unsigned 64-bit integer U64(u64), - /// 32-bit floating point value Float32(u32), - /// 64-bit floating point value Float64(u64), - /// 32-bit character Char(char), - /// Character string String(Box), - /// List of values List(List), - /// Record Record(Record), - /// Tuple Tuple(Tuple), - /// Variant Variant(Variant), - /// Enum Enum(Enum), - /// Union Union(Union), - /// Option - Option(Option), - /// Expected - Expected(Expected), - /// Bit flags + Option(OptionVal), + Result(ResultVal), Flags(Flags), } @@ -540,7 +518,6 @@ impl Val { /// Retrieve the [`Type`] of this value. pub fn ty(&self) -> Type { match self { - Val::Unit => Type::Unit, Val::Bool(_) => Type::Bool, Val::S8(_) => Type::S8, Val::U8(_) => Type::U8, @@ -560,8 +537,8 @@ impl Val { Val::Variant(Variant { ty, .. }) => Type::Variant(ty.clone()), Val::Enum(Enum { ty, .. }) => Type::Enum(ty.clone()), Val::Union(Union { ty, .. }) => Type::Union(ty.clone()), - Val::Option(Option { ty, .. }) => Type::Option(ty.clone()), - Val::Expected(Expected { ty, .. }) => Type::Expected(ty.clone()), + Val::Option(OptionVal { ty, .. }) => Type::Option(ty.clone()), + Val::Result(ResultVal { ty, .. }) => Type::Result(ty.clone()), Val::Flags(Flags { ty, .. }) => Type::Flags(ty.clone()), } } @@ -574,7 +551,6 @@ impl Val { src: &mut std::slice::Iter<'_, ValRaw>, ) -> Result { Ok(match ty { - Type::Unit => Val::Unit, Type::Bool => Val::Bool(bool::lift(store, options, next(src))?), Type::S8 => Val::S8(i8::lift(store, options, next(src))?), Type::U8 => Val::U8(u8::lift(store, options, next(src))?), @@ -622,13 +598,13 @@ impl Val { Val::Variant(Variant { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Enum(handle) => { let (discriminant, _) = lift_variant( handle.canonical_abi().flat_count(usize::MAX).unwrap(), - handle.names().map(|_| Type::Unit), + handle.names().map(|_| None), store, options, src, @@ -642,7 +618,7 @@ impl Val { Type::Union(handle) => { let (discriminant, value) = lift_variant( handle.canonical_abi().flat_count(usize::MAX).unwrap(), - handle.types(), + handle.types().map(Some), store, options, src, @@ -651,25 +627,25 @@ impl Val { Val::Union(Union { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Option(handle) => { let (discriminant, value) = lift_variant( handle.canonical_abi().flat_count(usize::MAX).unwrap(), - [Type::Unit, handle.ty()].into_iter(), + [None, Some(handle.ty())].into_iter(), store, options, src, )?; - Val::Option(Option { + Val::Option(OptionVal { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } - Type::Expected(handle) => { + Type::Result(handle) => { let (discriminant, value) = lift_variant( handle.canonical_abi().flat_count(usize::MAX).unwrap(), [handle.ok(), handle.err()].into_iter(), @@ -678,10 +654,10 @@ impl Val { src, )?; - Val::Expected(Expected { + Val::Result(ResultVal { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Flags(handle) => { @@ -703,7 +679,6 @@ impl Val { /// Deserialize a value of this type from the heap. pub(crate) fn load(ty: &Type, mem: &Memory, bytes: &[u8]) -> Result { Ok(match ty { - Type::Unit => Val::Unit, Type::Bool => Val::Bool(bool::load(mem, bytes)?), Type::S8 => Val::S8(i8::load(mem, bytes)?), Type::U8 => Val::U8(u8::load(mem, bytes)?), @@ -742,13 +717,13 @@ impl Val { Val::Variant(Variant { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Enum(handle) => { let (discriminant, _) = load_variant( handle.variant_info(), - handle.names().map(|_| Type::Unit), + handle.names().map(|_| None), mem, bytes, )?; @@ -760,29 +735,29 @@ impl Val { } Type::Union(handle) => { let (discriminant, value) = - load_variant(handle.variant_info(), handle.types(), mem, bytes)?; + load_variant(handle.variant_info(), handle.types().map(Some), mem, bytes)?; Val::Union(Union { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Option(handle) => { let (discriminant, value) = load_variant( handle.variant_info(), - [Type::Unit, handle.ty()].into_iter(), + [None, Some(handle.ty())].into_iter(), mem, bytes, )?; - Val::Option(Option { + Val::Option(OptionVal { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } - Type::Expected(handle) => { + Type::Result(handle) => { let (discriminant, value) = load_variant( handle.variant_info(), [handle.ok(), handle.err()].into_iter(), @@ -790,10 +765,10 @@ impl Val { bytes, )?; - Val::Expected(Expected { + Val::Result(ResultVal { ty: handle.clone(), discriminant, - value: Box::new(value), + value, }) } Type::Flags(handle) => Val::Flags(Flags { @@ -819,7 +794,6 @@ impl Val { dst: &mut std::slice::IterMut<'_, MaybeUninit>, ) -> Result<()> { match self { - Val::Unit => (), Val::Bool(value) => value.lower(store, options, next_mut(dst))?, Val::S8(value) => value.lower(store, options, next_mut(dst))?, Val::U8(value) => value.lower(store, options, next_mut(dst))?, @@ -863,23 +837,28 @@ impl Val { value, .. }) - | Val::Option(Option { + | Val::Option(OptionVal { discriminant, value, .. }) - | Val::Expected(Expected { + | Val::Result(ResultVal { discriminant, value, .. }) => { next_mut(dst).write(ValRaw::u32(*discriminant)); - value.lower(store, options, dst)?; // For the remaining lowered representation of this variant that // the payload didn't write we write out zeros here to ensure // the entire variant is written. - let value_flat = value.ty().canonical_abi().flat_count(usize::MAX).unwrap(); + let value_flat = match value { + Some(value) => { + value.lower(store, options, dst)?; + value.ty().canonical_abi().flat_count(usize::MAX).unwrap() + } + None => 0, + }; let variant_flat = self.ty().canonical_abi().flat_count(usize::MAX).unwrap(); for _ in (1 + value_flat)..variant_flat { next_mut(dst).write(ValRaw::u64(0)); @@ -903,7 +882,6 @@ impl Val { debug_assert!(offset % usize::try_from(self.ty().canonical_abi().align32)? == 0); match self { - Val::Unit => (), Val::Bool(value) => value.store(mem, offset)?, Val::S8(value) => value.store(mem, offset)?, Val::U8(value) => value.store(mem, offset)?, @@ -936,29 +914,53 @@ impl Val { discriminant, value, ty, - }) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?, + }) => self.store_variant( + *discriminant, + value.as_deref(), + ty.variant_info(), + mem, + offset, + )?, Val::Enum(Enum { discriminant, ty }) => { - self.store_variant(*discriminant, &Val::Unit, ty.variant_info(), mem, offset)? + self.store_variant(*discriminant, None, ty.variant_info(), mem, offset)? } Val::Union(Union { discriminant, value, ty, - }) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?, - - Val::Option(Option { + }) => self.store_variant( + *discriminant, + value.as_deref(), + ty.variant_info(), + mem, + offset, + )?, + + Val::Option(OptionVal { discriminant, value, ty, - }) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?, - - Val::Expected(Expected { + }) => self.store_variant( + *discriminant, + value.as_deref(), + ty.variant_info(), + mem, + offset, + )?, + + Val::Result(ResultVal { discriminant, value, ty, - }) => self.store_variant(*discriminant, value, ty.variant_info(), mem, offset)?, + }) => self.store_variant( + *discriminant, + value.as_deref(), + ty.variant_info(), + mem, + offset, + )?, Val::Flags(Flags { count, value, .. }) => { match FlagsSize::from_count(*count as usize) { @@ -982,7 +984,7 @@ impl Val { fn store_variant( &self, discriminant: u32, - value: &Val, + value: Option<&Val>, info: &VariantInfo, mem: &mut MemoryMut<'_, T>, offset: usize, @@ -993,8 +995,12 @@ impl Val { DiscriminantSize::Size4 => discriminant.store(mem, offset)?, } - let offset = offset + usize::try_from(info.payload_offset32).unwrap(); - value.store(mem, offset) + if let Some(value) = value { + let offset = offset + usize::try_from(info.payload_offset32).unwrap(); + value.store(mem, offset)?; + } + + Ok(()) } } @@ -1048,10 +1054,10 @@ fn load_record( fn load_variant( info: &VariantInfo, - mut types: impl ExactSizeIterator, + mut types: impl ExactSizeIterator>, mem: &Memory, bytes: &[u8], -) -> Result<(u32, Val)> { +) -> Result<(u32, Option>)> { let discriminant = match info.size { DiscriminantSize::Size1 => u32::from(u8::load(mem, &bytes[..1])?), DiscriminantSize::Size2 => u32::from(u16::load(mem, &bytes[..2])?), @@ -1064,26 +1070,40 @@ fn load_variant( types.len() ) })?; - let payload_offset = usize::try_from(info.payload_offset32).unwrap(); - let case_size = usize::try_from(case_ty.canonical_abi().size32).unwrap(); - let value = Val::load(&case_ty, mem, &bytes[payload_offset..][..case_size])?; + let value = match case_ty { + Some(case_ty) => { + let payload_offset = usize::try_from(info.payload_offset32).unwrap(); + let case_size = usize::try_from(case_ty.canonical_abi().size32).unwrap(); + Some(Box::new(Val::load( + &case_ty, + mem, + &bytes[payload_offset..][..case_size], + )?)) + } + None => None, + }; Ok((discriminant, value)) } fn lift_variant<'a>( flatten_count: usize, - mut types: impl ExactSizeIterator, + mut types: impl ExactSizeIterator>, store: &StoreOpaque, options: &Options, src: &mut std::slice::Iter<'_, ValRaw>, -) -> Result<(u32, Val)> { +) -> Result<(u32, Option>)> { let len = types.len(); let discriminant = next(src).get_u32(); let ty = types .nth(discriminant as usize) .ok_or_else(|| anyhow!("discriminant {} out of range [0..{})", discriminant, len))?; - let value = Val::lift(&ty, store, options, src)?; - let value_flat = ty.canonical_abi().flat_count(usize::MAX).unwrap(); + let (value, value_flat) = match ty { + Some(ty) => ( + Some(Box::new(Val::lift(&ty, store, options, src)?)), + ty.canonical_abi().flat_count(usize::MAX).unwrap(), + ), + None => (None, 0), + }; for _ in (1 + value_flat)..flatten_count { next(src); } diff --git a/crates/wast/src/component.rs b/crates/wast/src/component.rs index d2ee07c7058c..8a88538bb01e 100644 --- a/crates/wast/src/component.rs +++ b/crates/wast/src/component.rs @@ -9,7 +9,6 @@ pub use wasmtime::component::*; pub fn val(v: &WastVal<'_>, ty: &Type) -> Result { Ok(match v { - WastVal::Unit => Val::Unit, WastVal::Bool(b) => Val::Bool(*b), WastVal::U8(b) => Val::U8(*b), WastVal::S8(b) => Val::S8(*b), @@ -80,10 +79,10 @@ pub fn val(v: &WastVal<'_>, ty: &Type) -> Result { WastVal::Variant(name, payload) => match ty { Type::Variant(t) => { let case = match t.cases().find(|c| c.name == *name) { - Some(case) => case.ty, + Some(case) => case, None => bail!("no case named `{}", name), }; - let payload = val(payload, &case)?; + let payload = payload_val(case.name, payload.as_deref(), case.ty.as_ref())?; t.new_val(name, payload)? } _ => bail!("expected a variant value"), @@ -109,11 +108,11 @@ pub fn val(v: &WastVal<'_>, ty: &Type) -> Result { } _ => bail!("expected an option value"), }, - WastVal::Expected(v) => match ty { - Type::Expected(t) => { + WastVal::Result(v) => match ty { + Type::Result(t) => { let v = match v { - Ok(v) => Ok(val(v, &t.ok())?), - Err(v) => Err(val(v, &t.err())?), + Ok(v) => Ok(payload_val("ok", v.as_deref(), t.ok().as_ref())?), + Err(v) => Err(payload_val("err", v.as_deref(), t.err().as_ref())?), }; t.new_val(v)? } @@ -126,12 +125,17 @@ pub fn val(v: &WastVal<'_>, ty: &Type) -> Result { }) } +fn payload_val(name: &str, v: Option<&WastVal<'_>>, ty: Option<&Type>) -> Result> { + match (v, ty) { + (Some(v), Some(ty)) => Ok(Some(val(v, ty)?)), + (None, None) => Ok(None), + (Some(_), None) => bail!("expected payload for case `{name}`"), + (None, Some(_)) => bail!("unexpected payload for case `{name}`"), + } +} + pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { match expected { - WastVal::Unit => match actual { - Val::Unit => Ok(()), - _ => mismatch(expected, actual), - }, WastVal::Bool(e) => match actual { Val::Bool(a) => match_debug(a, e), _ => mismatch(expected, actual), @@ -242,7 +246,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { if a.discriminant() != *name { bail!("expected discriminant `{name}` got `{}`", a.discriminant()); } - match_val(e, a.payload()) + match_payload_val(name, e.as_deref(), a.payload()) } _ => mismatch(expected, actual), }, @@ -274,12 +278,12 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { }, _ => mismatch(expected, actual), }, - WastVal::Expected(e) => match actual { - Val::Expected(a) => match (e, a.value()) { + WastVal::Result(e) => match actual { + Val::Result(a) => match (e, a.value()) { (Ok(_), Err(_)) => bail!("expected `ok`, found `err`"), (Err(_), Ok(_)) => bail!("expected `err`, found `ok`"), - (Err(e), Err(a)) => match_val(e, a), - (Ok(e), Ok(a)) => match_val(e, a), + (Err(e), Err(a)) => match_payload_val("err", e.as_deref(), a), + (Ok(e), Ok(a)) => match_payload_val("ok", e.as_deref(), a), }, _ => mismatch(expected, actual), }, @@ -294,6 +298,21 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> { } } +fn match_payload_val( + name: &str, + expected: Option<&WastVal<'_>>, + actual: Option<&Val>, +) -> Result<()> { + match (expected, actual) { + (Some(e), Some(a)) => { + match_val(e, a).with_context(|| format!("failed to match case `{name}`")) + } + (None, None) => Ok(()), + (Some(_), None) => bail!("expected payload for case `{name}`"), + (None, Some(_)) => bail!("unexpected payload for case `{name}`"), + } +} + fn match_debug(actual: &T, expected: &T) -> Result<()> where T: Eq + Debug + ?Sized, @@ -311,7 +330,6 @@ where fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> { let expected = match expected { - WastVal::Unit => "unit", WastVal::Bool(..) => "bool", WastVal::U8(..) => "u8", WastVal::S8(..) => "s8", @@ -332,11 +350,10 @@ fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> { WastVal::Variant(..) => "variant", WastVal::Union(..) => "union", WastVal::Option(..) => "option", - WastVal::Expected(..) => "expected", + WastVal::Result(..) => "result", WastVal::Flags(..) => "flags", }; let actual = match actual { - Val::Unit => "unit", Val::Bool(..) => "bool", Val::U8(..) => "u8", Val::S8(..) => "s8", @@ -357,7 +374,7 @@ fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> { Val::Variant(..) => "variant", Val::Union(..) => "union", Val::Option(..) => "option", - Val::Expected(..) => "expected", + Val::Result(..) => "result", Val::Flags(..) => "flags", }; bail!("expected `{expected}` got `{actual}`") diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs index 74905d02dc52..a02d86970fd6 100644 --- a/crates/wast/src/spectest.rs +++ b/crates/wast/src/spectest.rs @@ -48,11 +48,11 @@ pub fn link_spectest(linker: &mut Linker, store: &mut Store) -> Result< #[cfg(feature = "component-model")] pub fn link_component_spectest(linker: &mut component::Linker) -> Result<()> { let engine = linker.engine().clone(); - linker.root().func_wrap("host-return-two", || Ok(2u32))?; + linker.root().func_wrap("host-return-two", || Ok((2u32,)))?; let mut i = linker.instance("host")?; - i.func_wrap("return-three", || Ok(3u32))?; + i.func_wrap("return-three", || Ok((3u32,)))?; i.instance("nested")? - .func_wrap("return-four", || Ok(4u32))?; + .func_wrap("return-four", || Ok((4u32,)))?; let module = Module::new( &engine, diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index d9f885acff28..6980a0317d65 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -47,7 +47,7 @@ impl Outcome { enum Results { Core(Vec), #[cfg(feature = "component-model")] - Component(component::Val), + Component(Vec), } enum InstanceKind { @@ -148,7 +148,7 @@ impl WastContext { #[cfg(feature = "component-model")] Wat::Component(m) => self .instantiate_component(&m.encode()?)? - .map(|_| Results::Component(component::Val::Unit)), + .map(|_| Results::Component(Vec::new())), #[cfg(not(feature = "component-model"))] Wat::Component(_) => bail!("component-model support not enabled"), }), @@ -193,8 +193,10 @@ impl WastContext { }) .collect::>>()?; - Ok(match func.call(&mut self.store, &values) { - Ok(results) => { + let mut results = + vec![component::Val::Bool(false); func.results(&self.store).len()]; + Ok(match func.call(&mut self.store, &values, &mut results) { + Ok(()) => { func.post_return(&mut self.store)?; Outcome::Ok(Results::Component(results.into())) } @@ -290,6 +292,9 @@ impl WastContext { fn assert_return(&self, result: Outcome, results: &[WastRet<'_>]) -> Result<()> { match result.into_result()? { Results::Core(values) => { + if values.len() != results.len() { + bail!("expected {} results found {}", results.len(), values.len()); + } for (i, (v, e)) in values.iter().zip(results).enumerate() { let e = match e { WastRet::Core(core) => core, @@ -301,17 +306,20 @@ impl WastContext { } } #[cfg(feature = "component-model")] - Results::Component(value) => { - if results.len() != 1 { - bail!("expected one result value assertion"); + Results::Component(values) => { + if values.len() != results.len() { + bail!("expected {} results found {}", results.len(), values.len()); + } + for (i, (v, e)) in values.iter().zip(results).enumerate() { + let e = match e { + WastRet::Core(_) => { + bail!("expected component value found core value") + } + WastRet::Component(val) => val, + }; + component::match_val(e, v) + .with_context(|| format!("result {} didn't match", i))?; } - let result = match &results[0] { - WastRet::Component(ret) => ret, - WastRet::Core(_) => { - bail!("expected core value found component value") - } - }; - component::match_val(&result, &value)?; } } Ok(()) diff --git a/fuzz/build.rs b/fuzz/build.rs index 97c6e64ddffd..9e737857c48f 100644 --- a/fuzz/build.rs +++ b/fuzz/build.rs @@ -75,7 +75,7 @@ mod component { let Declarations { types, params, - result, + results, import_and_export, encoding1, encoding2, @@ -92,16 +92,22 @@ mod component { }) .collect::(); - let rust_result = - component_fuzz_util::rust_type(&case.result, name_counter, &mut declarations); + let rust_results = case + .results + .iter() + .map(|ty| { + let ty = component_fuzz_util::rust_type(&ty, name_counter, &mut declarations); + quote!(#ty,) + }) + .collect::(); - let test = quote!(#index => component_types::#test::<#rust_params #rust_result>( + let test = quote!(#index => component_types::#test::<#rust_params (#rust_results)>( input, { static DECLS: Declarations = Declarations { types: Cow::Borrowed(#types), params: Cow::Borrowed(#params), - result: Cow::Borrowed(#result), + results: Cow::Borrowed(#results), import_and_export: Cow::Borrowed(#import_and_export), encoding1: #encoding1, encoding2: #encoding2, diff --git a/tests/all/component_model/dynamic.rs b/tests/all/component_model/dynamic.rs index f9ac63fdb871..fd9e19c5c4a5 100644 --- a/tests/all/component_model/dynamic.rs +++ b/tests/all/component_model/dynamic.rs @@ -8,6 +8,7 @@ use wasmtime::Store; fn primitives() -> Result<()> { let engine = super::engine(); let mut store = Store::new(&engine, ()); + let mut output = [Val::Bool(false)]; for (input, ty, param) in [ (Val::Bool(true), "bool", Param(Type::U8, Some(0))), @@ -34,9 +35,9 @@ fn primitives() -> Result<()> { let component = Component::new(&engine, make_echo_component_with_params(ty, &[param]))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); } // Sad path: type mismatch @@ -48,7 +49,7 @@ fn primitives() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let err = func - .call_and_post_return(&mut store, &[Val::U64(42)]) + .call_and_post_return(&mut store, &[Val::U64(42)], &mut output) .unwrap_err(); assert!(err.to_string().contains("type mismatch"), "{err}"); @@ -62,6 +63,7 @@ fn primitives() -> Result<()> { Val::Float64(3.14159265_f64.to_bits()), Val::Float64(3.14159265_f64.to_bits()), ], + &mut output, ) .unwrap_err(); @@ -72,13 +74,22 @@ fn primitives() -> Result<()> { // Sad path: arity mismatch (too few) - let err = func.call_and_post_return(&mut store, &[]).unwrap_err(); - + let err = func + .call_and_post_return(&mut store, &[], &mut output) + .unwrap_err(); assert!( err.to_string().contains("expected 1 argument(s), got 0"), "{err}" ); + let err = func + .call_and_post_return(&mut store, &output, &mut []) + .unwrap_err(); + assert!( + err.to_string().contains("expected 1 results(s), got 0"), + "{err}" + ); + Ok(()) } @@ -91,9 +102,9 @@ fn strings() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let input = Val::String(Box::from("hello, component!")); - let output = func.call_and_post_return(&mut store, &[input.clone()])?; - - assert_eq!(input, output); + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; + assert_eq!(input, output[0]); Ok(()) } @@ -112,9 +123,10 @@ fn lists() -> Result<()> { Val::U32(79023439), Val::U32(2084037802), ]))?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Sad path: type mismatch @@ -163,9 +175,10 @@ fn records() -> Result<()> { .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?, ), ])?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Sad path: type mismatch @@ -198,7 +211,7 @@ fn records() -> Result<()> { .unwrap_record() .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?, ), - ("F", Val::Unit), + ("F", Val::Bool(true)), ]) .unwrap_err(); @@ -246,54 +259,62 @@ fn variants() -> Result<()> { let ty = &func.params(&store)[0]; let input = ty .unwrap_variant() - .new_val("B", Val::Float64(3.14159265_f64.to_bits()))?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + .new_val("B", Some(Val::Float64(3.14159265_f64.to_bits())))?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Do it again, this time using case "C" let component = Component::new( &engine, - dbg!(make_echo_component_with_params( + make_echo_component_with_params( r#"(variant (case "A" u32) (case "B" float64) (case "C" (record (field "D" bool) (field "E" u32))))"#, &[ Param(Type::U8, Some(0)), Param(Type::I64, Some(8)), Param(Type::I32, Some(12)), ], - )), + ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "echo").unwrap(); let ty = &func.params(&store)[0]; - let c_type = &ty.unwrap_variant().cases().nth(2).unwrap().ty; + let c_type = &ty.unwrap_variant().cases().nth(2).unwrap().ty.unwrap(); let input = ty.unwrap_variant().new_val( "C", - c_type - .unwrap_record() - .new_val([("D", Val::Bool(true)), ("E", Val::U32(314159265))])?, + Some( + c_type + .unwrap_record() + .new_val([("D", Val::Bool(true)), ("E", Val::U32(314159265))])?, + ), )?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Sad path: type mismatch let err = ty .unwrap_variant() - .new_val("B", Val::U64(314159265)) + .new_val("B", Some(Val::U64(314159265))) .unwrap_err(); - assert!(err.to_string().contains("type mismatch"), "{err}"); + let err = ty.unwrap_variant().new_val("B", None).unwrap_err(); + assert!( + err.to_string().contains("expected a payload for case `B`"), + "{err}" + ); // Sad path: unknown case let err = ty .unwrap_variant() - .new_val("D", Val::U64(314159265)) + .new_val("D", Some(Val::U64(314159265))) .unwrap_err(); - + assert!(err.to_string().contains("unknown variant case"), "{err}"); + let err = ty.unwrap_variant().new_val("D", None).unwrap_err(); assert!(err.to_string().contains("unknown variant case"), "{err}"); // Make sure we lift variants which have cases of different sizes with the correct alignment @@ -323,13 +344,15 @@ fn variants() -> Result<()> { let input = ty.unwrap_record().new_val([ ( "A", - a_type.unwrap_variant().new_val("A", Val::U32(314159265))?, + a_type + .unwrap_variant() + .new_val("A", Some(Val::U32(314159265)))?, ), ("B", Val::U32(628318530)), ])?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); Ok(()) } @@ -350,9 +373,10 @@ fn flags() -> Result<()> { let func = instance.get_func(&mut store, "echo").unwrap(); let ty = &func.params(&store)[0]; let input = ty.unwrap_flags().new_val(&["B", "D"])?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); // Sad path: unknown flags @@ -391,11 +415,10 @@ fn everything() -> Result<()> { (field "U" float64) (field "V" string) (field "W" char) - (field "X" unit) (field "Y" (tuple u32 u32)) (field "Z" (union u32 float64)) (field "AA" (option u32)) - (field "BB" (expected string string)) + (field "BB" (result string (error string))) )"#, &[ Param(Type::I32, Some(0)), @@ -436,7 +459,7 @@ fn everything() -> Result<()> { .map(|field| field.ty) .collect::>(); let (b_type, c_type, f_type, j_type, y_type, z_type, aa_type, bb_type) = ( - &types[1], &types[2], &types[3], &types[4], &types[14], &types[15], &types[16], &types[17], + &types[1], &types[2], &types[3], &types[4], &types[13], &types[14], &types[15], &types[16], ); let f_element_type = &f_type.unwrap_list().ty(); let input = ty.unwrap_record().new_val([ @@ -458,7 +481,7 @@ fn everything() -> Result<()> { "J", j_type .unwrap_variant() - .new_val("L", Val::Float64(3.14159265_f64.to_bits()))?, + .new_val("L", Some(Val::Float64(3.14159265_f64.to_bits())))?, ), ("P", Val::S8(42)), ("Q", Val::S16(4242)), @@ -468,7 +491,6 @@ fn everything() -> Result<()> { ("U", Val::Float64(3.14159265_f64.to_bits())), ("V", Val::String(Box::from("wow, nice types"))), ("W", Val::Char('🦀')), - ("X", Val::Unit), ( "Y", y_type @@ -488,13 +510,14 @@ fn everything() -> Result<()> { ( "BB", bb_type - .unwrap_expected() - .new_val(Ok(Val::String(Box::from("no problem"))))?, + .unwrap_result() + .new_val(Ok(Some(Val::String(Box::from("no problem")))))?, ), ])?; - let output = func.call_and_post_return(&mut store, &[input.clone()])?; + let mut output = [Val::Bool(false)]; + func.call_and_post_return(&mut store, &[input.clone()], &mut output)?; - assert_eq!(input, output); + assert_eq!(input, output[0]); Ok(()) } diff --git a/tests/all/component_model/func.rs b/tests/all/component_model/func.rs index 6c30bd985d03..cf28ea5c5012 100644 --- a/tests/all/component_model/func.rs +++ b/tests/all/component_model/func.rs @@ -66,10 +66,10 @@ fn typecheck() -> Result<()> { (func (export "take-string") (param string) (canon lift (core func $i "take-string") (memory $i "memory") (realloc (func $i "realloc"))) ) - (func (export "take-two-args") (param s32) (param (list u8)) + (func (export "take-two-args") (param "a" s32) (param "b" (list u8)) (canon lift (core func $i "two-args") (memory $i "memory") (realloc (func $i "realloc"))) ) - (func (export "ret-tuple") (result (tuple u8 s8)) + (func (export "ret-tuple") (result "a" u8) (result "b" s8) (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc"))) ) (func (export "ret-tuple1") (result (tuple u32)) @@ -96,29 +96,34 @@ fn typecheck() -> Result<()> { let ret_tuple1 = instance.get_func(&mut store, "ret-tuple1").unwrap(); let ret_string = instance.get_func(&mut store, "ret-string").unwrap(); let ret_list_u8 = instance.get_func(&mut store, "ret-list-u8").unwrap(); - assert!(thunk.typed::<(), u32, _>(&store).is_err()); + assert!(thunk.typed::<(), (u32,), _>(&store).is_err()); assert!(thunk.typed::<(u32,), (), _>(&store).is_err()); assert!(thunk.typed::<(), (), _>(&store).is_ok()); assert!(tuple_thunk.typed::<(), (), _>(&store).is_err()); - assert!(tuple_thunk.typed::<((),), (), _>(&store).is_ok()); + assert!(tuple_thunk.typed::<((),), (), _>(&store).is_err()); + assert!(tuple_thunk.typed::<((),), ((),), _>(&store).is_ok()); assert!(take_string.typed::<(), (), _>(&store).is_err()); assert!(take_string.typed::<(String,), (), _>(&store).is_ok()); assert!(take_string.typed::<(&str,), (), _>(&store).is_ok()); assert!(take_string.typed::<(&[u8],), (), _>(&store).is_err()); assert!(take_two_args.typed::<(), (), _>(&store).is_err()); - assert!(take_two_args.typed::<(i32, &[u8]), u32, _>(&store).is_err()); + assert!(take_two_args + .typed::<(i32, &[u8]), (u32,), _>(&store) + .is_err()); assert!(take_two_args.typed::<(u32, &[u8]), (), _>(&store).is_err()); assert!(take_two_args.typed::<(i32, &[u8]), (), _>(&store).is_ok()); assert!(ret_tuple.typed::<(), (), _>(&store).is_err()); assert!(ret_tuple.typed::<(), (u8,), _>(&store).is_err()); assert!(ret_tuple.typed::<(), (u8, i8), _>(&store).is_ok()); - assert!(ret_tuple1.typed::<(), (u32,), _>(&store).is_ok()); - assert!(ret_tuple1.typed::<(), u32, _>(&store).is_err()); + assert!(ret_tuple1.typed::<(), ((u32,),), _>(&store).is_ok()); + assert!(ret_tuple1.typed::<(), (u32,), _>(&store).is_err()); assert!(ret_string.typed::<(), (), _>(&store).is_err()); - assert!(ret_string.typed::<(), WasmStr, _>(&store).is_ok()); - assert!(ret_list_u8.typed::<(), WasmList, _>(&store).is_err()); - assert!(ret_list_u8.typed::<(), WasmList, _>(&store).is_err()); - assert!(ret_list_u8.typed::<(), WasmList, _>(&store).is_ok()); + assert!(ret_string.typed::<(), (WasmStr,), _>(&store).is_ok()); + assert!(ret_list_u8 + .typed::<(), (WasmList,), _>(&store) + .is_err()); + assert!(ret_list_u8.typed::<(), (WasmList,), _>(&store).is_err()); + assert!(ret_list_u8.typed::<(), (WasmList,), _>(&store).is_ok()); Ok(()) } @@ -262,140 +267,140 @@ fn integers() -> Result<()> { // Zero can be returned as any integer assert_eq!( instance - .get_typed_func::<(), u8, _>(&mut store, "ret-u8")? + .get_typed_func::<(), (u8,), _>(&mut store, "ret-u8")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), i8, _>(&mut store, "ret-s8")? + .get_typed_func::<(), (i8,), _>(&mut store, "ret-s8")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), u16, _>(&mut store, "ret-u16")? + .get_typed_func::<(), (u16,), _>(&mut store, "ret-u16")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), i16, _>(&mut store, "ret-s16")? + .get_typed_func::<(), (i16,), _>(&mut store, "ret-s16")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), u32, _>(&mut store, "ret-u32")? + .get_typed_func::<(), (u32,), _>(&mut store, "ret-u32")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), i32, _>(&mut store, "ret-s32")? + .get_typed_func::<(), (i32,), _>(&mut store, "ret-s32")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), u64, _>(&mut store, "ret-u64")? + .get_typed_func::<(), (u64,), _>(&mut store, "ret-u64")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); assert_eq!( instance - .get_typed_func::<(), i64, _>(&mut store, "ret-s64")? + .get_typed_func::<(), (i64,), _>(&mut store, "ret-s64")? .call_and_post_return(&mut store, ())?, - 0 + (0,) ); // Returning -1 should reinterpret the bytes as defined by each type. assert_eq!( instance - .get_typed_func::<(), u8, _>(&mut store, "retm1-u8")? + .get_typed_func::<(), (u8,), _>(&mut store, "retm1-u8")? .call_and_post_return(&mut store, ())?, - 0xff + (0xff,) ); assert_eq!( instance - .get_typed_func::<(), i8, _>(&mut store, "retm1-s8")? + .get_typed_func::<(), (i8,), _>(&mut store, "retm1-s8")? .call_and_post_return(&mut store, ())?, - -1 + (-1,) ); assert_eq!( instance - .get_typed_func::<(), u16, _>(&mut store, "retm1-u16")? + .get_typed_func::<(), (u16,), _>(&mut store, "retm1-u16")? .call_and_post_return(&mut store, ())?, - 0xffff + (0xffff,) ); assert_eq!( instance - .get_typed_func::<(), i16, _>(&mut store, "retm1-s16")? + .get_typed_func::<(), (i16,), _>(&mut store, "retm1-s16")? .call_and_post_return(&mut store, ())?, - -1 + (-1,) ); assert_eq!( instance - .get_typed_func::<(), u32, _>(&mut store, "retm1-u32")? + .get_typed_func::<(), (u32,), _>(&mut store, "retm1-u32")? .call_and_post_return(&mut store, ())?, - 0xffffffff + (0xffffffff,) ); assert_eq!( instance - .get_typed_func::<(), i32, _>(&mut store, "retm1-s32")? + .get_typed_func::<(), (i32,), _>(&mut store, "retm1-s32")? .call_and_post_return(&mut store, ())?, - -1 + (-1,) ); assert_eq!( instance - .get_typed_func::<(), u64, _>(&mut store, "retm1-u64")? + .get_typed_func::<(), (u64,), _>(&mut store, "retm1-u64")? .call_and_post_return(&mut store, ())?, - 0xffffffff_ffffffff + (0xffffffff_ffffffff,) ); assert_eq!( instance - .get_typed_func::<(), i64, _>(&mut store, "retm1-s64")? + .get_typed_func::<(), (i64,), _>(&mut store, "retm1-s64")? .call_and_post_return(&mut store, ())?, - -1 + (-1,) ); // Returning 100000 should chop off bytes as necessary let ret: u32 = 100000; assert_eq!( instance - .get_typed_func::<(), u8, _>(&mut store, "retbig-u8")? + .get_typed_func::<(), (u8,), _>(&mut store, "retbig-u8")? .call_and_post_return(&mut store, ())?, - ret as u8, + (ret as u8,), ); assert_eq!( instance - .get_typed_func::<(), i8, _>(&mut store, "retbig-s8")? + .get_typed_func::<(), (i8,), _>(&mut store, "retbig-s8")? .call_and_post_return(&mut store, ())?, - ret as i8, + (ret as i8,), ); assert_eq!( instance - .get_typed_func::<(), u16, _>(&mut store, "retbig-u16")? + .get_typed_func::<(), (u16,), _>(&mut store, "retbig-u16")? .call_and_post_return(&mut store, ())?, - ret as u16, + (ret as u16,), ); assert_eq!( instance - .get_typed_func::<(), i16, _>(&mut store, "retbig-s16")? + .get_typed_func::<(), (i16,), _>(&mut store, "retbig-s16")? .call_and_post_return(&mut store, ())?, - ret as i16, + (ret as i16,), ); assert_eq!( instance - .get_typed_func::<(), u32, _>(&mut store, "retbig-u32")? + .get_typed_func::<(), (u32,), _>(&mut store, "retbig-u32")? .call_and_post_return(&mut store, ())?, - ret, + (ret,), ); assert_eq!( instance - .get_typed_func::<(), i32, _>(&mut store, "retbig-s32")? + .get_typed_func::<(), (i32,), _>(&mut store, "retbig-s32")? .call_and_post_return(&mut store, ())?, - ret as i32, + (ret as i32,), ); Ok(()) @@ -486,23 +491,24 @@ fn floats() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let f32_to_u32 = instance.get_typed_func::<(f32,), u32, _>(&mut store, "f32-to-u32")?; - let f64_to_u64 = instance.get_typed_func::<(f64,), u64, _>(&mut store, "f64-to-u64")?; - let u32_to_f32 = instance.get_typed_func::<(u32,), f32, _>(&mut store, "u32-to-f32")?; - let u64_to_f64 = instance.get_typed_func::<(u64,), f64, _>(&mut store, "u64-to-f64")?; + let f32_to_u32 = instance.get_typed_func::<(f32,), (u32,), _>(&mut store, "f32-to-u32")?; + let f64_to_u64 = instance.get_typed_func::<(f64,), (u64,), _>(&mut store, "f64-to-u64")?; + let u32_to_f32 = instance.get_typed_func::<(u32,), (f32,), _>(&mut store, "u32-to-f32")?; + let u64_to_f64 = instance.get_typed_func::<(u64,), (f64,), _>(&mut store, "u64-to-f64")?; - assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, 1.0f32.to_bits()); + assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, (1.0f32.to_bits(),)); f32_to_u32.post_return(&mut store)?; - assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, 2.0f64.to_bits()); + assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, (2.0f64.to_bits(),)); f64_to_u64.post_return(&mut store)?; - assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, 3.0); + assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, (3.0,)); u32_to_f32.post_return(&mut store)?; - assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, 4.0); + assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, (4.0,)); u64_to_f64.post_return(&mut store)?; assert_eq!( u32_to_f32 .call(&mut store, (CANON_32BIT_NAN | 1,))? + .0 .to_bits(), CANON_32BIT_NAN ); @@ -510,19 +516,20 @@ fn floats() -> Result<()> { assert_eq!( u64_to_f64 .call(&mut store, (CANON_64BIT_NAN | 1,))? + .0 .to_bits(), - CANON_64BIT_NAN + CANON_64BIT_NAN, ); u64_to_f64.post_return(&mut store)?; assert_eq!( f32_to_u32.call(&mut store, (f32::from_bits(CANON_32BIT_NAN | 1),))?, - CANON_32BIT_NAN + (CANON_32BIT_NAN,) ); f32_to_u32.post_return(&mut store)?; assert_eq!( f64_to_u64.call(&mut store, (f64::from_bits(CANON_64BIT_NAN | 1),))?, - CANON_64BIT_NAN + (CANON_64BIT_NAN,) ); f64_to_u64.post_return(&mut store)?; @@ -551,18 +558,18 @@ fn bools() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let u32_to_bool = instance.get_typed_func::<(u32,), bool, _>(&mut store, "u32-to-bool")?; - let bool_to_u32 = instance.get_typed_func::<(bool,), u32, _>(&mut store, "bool-to-u32")?; + let u32_to_bool = instance.get_typed_func::<(u32,), (bool,), _>(&mut store, "u32-to-bool")?; + let bool_to_u32 = instance.get_typed_func::<(bool,), (u32,), _>(&mut store, "bool-to-u32")?; - assert_eq!(bool_to_u32.call(&mut store, (false,))?, 0); + assert_eq!(bool_to_u32.call(&mut store, (false,))?, (0,)); bool_to_u32.post_return(&mut store)?; - assert_eq!(bool_to_u32.call(&mut store, (true,))?, 1); + assert_eq!(bool_to_u32.call(&mut store, (true,))?, (1,)); bool_to_u32.post_return(&mut store)?; - assert_eq!(u32_to_bool.call(&mut store, (0,))?, false); + assert_eq!(u32_to_bool.call(&mut store, (0,))?, (false,)); u32_to_bool.post_return(&mut store)?; - assert_eq!(u32_to_bool.call(&mut store, (1,))?, true); + assert_eq!(u32_to_bool.call(&mut store, (1,))?, (true,)); u32_to_bool.post_return(&mut store)?; - assert_eq!(u32_to_bool.call(&mut store, (2,))?, true); + assert_eq!(u32_to_bool.call(&mut store, (2,))?, (true,)); u32_to_bool.post_return(&mut store)?; Ok(()) @@ -590,13 +597,13 @@ fn chars() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let u32_to_char = instance.get_typed_func::<(u32,), char, _>(&mut store, "u32-to-char")?; - let char_to_u32 = instance.get_typed_func::<(char,), u32, _>(&mut store, "char-to-u32")?; + let u32_to_char = instance.get_typed_func::<(u32,), (char,), _>(&mut store, "u32-to-char")?; + let char_to_u32 = instance.get_typed_func::<(char,), (u32,), _>(&mut store, "char-to-u32")?; let mut roundtrip = |x: char| -> Result<()> { - assert_eq!(char_to_u32.call(&mut store, (x,))?, x as u32); + assert_eq!(char_to_u32.call(&mut store, (x,))?, (x as u32,)); char_to_u32.post_return(&mut store)?; - assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, x); + assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, (x,)); u32_to_char.post_return(&mut store)?; Ok(()) }; @@ -610,7 +617,7 @@ fn chars() -> Result<()> { let u32_to_char = |store: &mut Store<()>| { Linker::new(&engine) .instantiate(&mut *store, &component)? - .get_typed_func::<(u32,), char, _>(&mut *store, "u32-to-char") + .get_typed_func::<(u32,), (char,), _>(&mut *store, "u32-to-char") }; let err = u32_to_char(&mut store)? .call(&mut store, (0xd800,)) @@ -656,7 +663,7 @@ fn tuple_result() -> Result<()> { (type $result (tuple s8 u16 float32 float64)) (func (export "tuple") - (param s8) (param u16) (param float32) (param float64) (result $result) + (param "a" s8) (param "b" u16) (param "c" float32) (param "d" float64) (result $result) (canon lift (core func $i "foo") (memory $i "memory")) ) (func (export "invalid") (result $result) @@ -672,12 +679,12 @@ fn tuple_result() -> Result<()> { let input = (-1, 100, 3.0, 100.0); let output = instance - .get_typed_func::<(i8, u16, f32, f64), (i8, u16, f32, f64), _>(&mut store, "tuple")? + .get_typed_func::<(i8, u16, f32, f64), ((i8, u16, f32, f64),), _>(&mut store, "tuple")? .call_and_post_return(&mut store, input)?; - assert_eq!(input, output); + assert_eq!((input,), output); let invalid_func = - instance.get_typed_func::<(), (i8, u16, f32, f64), _>(&mut store, "invalid")?; + instance.get_typed_func::<(), ((i8, u16, f32, f64),), _>(&mut store, "invalid")?; let err = invalid_func.call(&mut store, ()).err().unwrap(); assert!( err.to_string().contains("pointer out of bounds of memory"), @@ -753,29 +760,29 @@ fn strings() -> Result<()> { let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let list8_to_str = - instance.get_typed_func::<(&[u8],), WasmStr, _>(&mut store, "list8-to-str")?; + instance.get_typed_func::<(&[u8],), (WasmStr,), _>(&mut store, "list8-to-str")?; let str_to_list8 = - instance.get_typed_func::<(&str,), WasmList, _>(&mut store, "str-to-list8")?; + instance.get_typed_func::<(&str,), (WasmList,), _>(&mut store, "str-to-list8")?; let list16_to_str = - instance.get_typed_func::<(&[u16],), WasmStr, _>(&mut store, "list16-to-str")?; + instance.get_typed_func::<(&[u16],), (WasmStr,), _>(&mut store, "list16-to-str")?; let str_to_list16 = - instance.get_typed_func::<(&str,), WasmList, _>(&mut store, "str-to-list16")?; + instance.get_typed_func::<(&str,), (WasmList,), _>(&mut store, "str-to-list16")?; let mut roundtrip = |x: &str| -> Result<()> { - let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?; + let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?.0; assert_eq!(ret.to_str(&store)?, x); list8_to_str.post_return(&mut store)?; let utf16 = x.encode_utf16().collect::>(); - let ret = list16_to_str.call(&mut store, (&utf16[..],))?; + let ret = list16_to_str.call(&mut store, (&utf16[..],))?.0; assert_eq!(ret.to_str(&store)?, x); list16_to_str.post_return(&mut store)?; - let ret = str_to_list8.call(&mut store, (x,))?; + let ret = str_to_list8.call(&mut store, (x,))?.0; assert_eq!(ret.iter(&store).collect::>>()?, x.as_bytes()); str_to_list8.post_return(&mut store)?; - let ret = str_to_list16.call(&mut store, (x,))?; + let ret = str_to_list16.call(&mut store, (x,))?.0; assert_eq!(ret.iter(&store).collect::>>()?, utf16,); str_to_list16.post_return(&mut store)?; @@ -788,27 +795,29 @@ fn strings() -> Result<()> { roundtrip("💝")?; roundtrip("Löwe 老虎 Léopard")?; - let ret = list8_to_str.call(&mut store, (b"\xff",))?; + let ret = list8_to_str.call(&mut store, (b"\xff",))?.0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("invalid utf-8"), "{}", err); list8_to_str.post_return(&mut store)?; - let ret = list8_to_str.call(&mut store, (b"hello there \xff invalid",))?; + let ret = list8_to_str + .call(&mut store, (b"hello there \xff invalid",))? + .0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("invalid utf-8"), "{}", err); list8_to_str.post_return(&mut store)?; - let ret = list16_to_str.call(&mut store, (&[0xd800],))?; + let ret = list16_to_str.call(&mut store, (&[0xd800],))?.0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("unpaired surrogate"), "{}", err); list16_to_str.post_return(&mut store)?; - let ret = list16_to_str.call(&mut store, (&[0xdfff],))?; + let ret = list16_to_str.call(&mut store, (&[0xdfff],))?.0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("unpaired surrogate"), "{}", err); list16_to_str.post_return(&mut store)?; - let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?; + let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?.0; let err = ret.to_str(&store).unwrap_err(); assert!(err.to_string().contains("unpaired surrogate"), "{}", err); list16_to_str.post_return(&mut store)?; @@ -856,24 +865,24 @@ fn many_parameters() -> Result<()> { ) (core instance $i (instantiate $m)) - (type $result (tuple (list u8) u32)) (type $t (func - (param s8) ;; offset 0, size 1 - (param u64) ;; offset 8, size 8 - (param float32) ;; offset 16, size 4 - (param u8) ;; offset 20, size 1 - (param unit) ;; offset 21, size 0 - (param s16) ;; offset 22, size 2 - (param string) ;; offset 24, size 8 - (param (list u32)) ;; offset 32, size 8 - (param bool) ;; offset 40, size 1 - (param bool) ;; offset 41, size 1 - (param char) ;; offset 44, size 4 - (param (list bool)) ;; offset 48, size 8 - (param (list char)) ;; offset 56, size 8 - (param (list string)) ;; offset 64, size 8 - - (result $result) + (param "p1" s8) ;; offset 0, size 1 + (param "p2" u64) ;; offset 8, size 8 + (param "p3" float32) ;; offset 16, size 4 + (param "p4" u8) ;; offset 20, size 1 + (param "p5" (tuple)) ;; offset 21, size 0 + (param "p6" s16) ;; offset 22, size 2 + (param "p7" string) ;; offset 24, size 8 + (param "p8" (list u32)) ;; offset 32, size 8 + (param "p9" bool) ;; offset 40, size 1 + (param "pa" bool) ;; offset 41, size 1 + (param "pb" char) ;; offset 44, size 4 + (param "pc" (list bool)) ;; offset 48, size 8 + (param "pd" (list char)) ;; offset 56, size 8 + (param "pe" (list string)) ;; offset 64, size 8 + + (result "all-memory" (list u8)) + (result "pointer" u32) )) (func (export "many-param") (type $t) (canon lift @@ -1000,16 +1009,16 @@ fn some_traps() -> Result<()> { ) (type $t (func - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) - (param string) + (param "s1" string) + (param "s2" string) + (param "s3" string) + (param "s4" string) + (param "s5" string) + (param "s6" string) + (param "s7" string) + (param "s8" string) + (param "s9" string) + (param "s10" string) )) (func (export "take-many-unreachable") (type $t) (canon lift (core func $i "take-many") (memory $i "memory") (realloc (func $i "realloc"))) @@ -1246,7 +1255,7 @@ fn char_bool_memory() -> Result<()> { ) (core instance $i (instantiate $m)) - (func (export "ret-tuple") (param u32) (param u32) (result (tuple bool char)) + (func (export "ret-tuple") (param "a" u32) (param "b" u32) (result "c" bool) (result "d" char) (canon lift (core func $i "ret-tuple") (memory $i "memory") (realloc (func $i "realloc"))) @@ -1328,10 +1337,10 @@ fn string_list_oob() -> Result<()> { let mut store = Store::new(&engine, ()); let ret_list_u8 = Linker::new(&engine) .instantiate(&mut store, &component)? - .get_typed_func::<(), WasmList, _>(&mut store, "ret-list-u8")?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "ret-list-u8")?; let ret_string = Linker::new(&engine) .instantiate(&mut store, &component)? - .get_typed_func::<(), WasmStr, _>(&mut store, "ret-string")?; + .get_typed_func::<(), (WasmStr,), _>(&mut store, "ret-string")?; let err = ret_list_u8.call(&mut store, ()).err().unwrap(); assert!(err.to_string().contains("out of bounds"), "{}", err); @@ -1373,8 +1382,8 @@ fn tuples() -> Result<()> { (core instance $i (instantiate $m)) (func (export "foo") - (param (tuple s32 float64)) - (param (tuple s8)) + (param "a" (tuple s32 float64)) + (param "b" (tuple s8)) (result (tuple u16)) (canon lift (core func $i "foo")) ) @@ -1385,8 +1394,8 @@ fn tuples() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let foo = instance.get_typed_func::<((i32, f64), (i8,)), (u16,), _>(&mut store, "foo")?; - assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, (3,)); + let foo = instance.get_typed_func::<((i32, f64), (i8,)), ((u16,),), _>(&mut store, "foo")?; + assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, ((3,),)); Ok(()) } @@ -1444,35 +1453,35 @@ fn option() -> Result<()> { ) (core instance $i (instantiate $m)) - (func (export "option-unit-to-u32") (param (option unit)) (result u32) + (func (export "option-unit-to-u32") (param (option (tuple))) (result u32) (canon lift (core func $i "pass0")) ) - (func (export "option-u8-to-tuple") (param (option u8)) (result (tuple u32 u32)) + (func (export "option-u8-to-tuple") (param (option u8)) (result "a" u32) (result "b" u32) (canon lift (core func $i "pass1") (memory $i "memory")) ) - (func (export "option-u32-to-tuple") (param (option u32)) (result (tuple u32 u32)) + (func (export "option-u32-to-tuple") (param (option u32)) (result "a" u32) (result "b" u32) (canon lift (core func $i "pass1") (memory $i "memory")) ) - (func (export "option-string-to-tuple") (param (option string)) (result (tuple u32 string)) + (func (export "option-string-to-tuple") (param (option string)) (result "a" u32) (result "b" string) (canon lift (core func $i "pass2") (memory $i "memory") (realloc (func $i "realloc")) ) ) - (func (export "to-option-unit") (param u32) (result (option unit)) + (func (export "to-option-unit") (param u32) (result (option (tuple))) (canon lift (core func $i "pass0")) ) - (func (export "to-option-u8") (param u32) (param u32) (result (option u8)) + (func (export "to-option-u8") (param "a" u32) (param "b" u32) (result (option u8)) (canon lift (core func $i "pass1") (memory $i "memory")) ) - (func (export "to-option-u32") (param u32) (param u32) (result (option u32)) + (func (export "to-option-u32") (param "a" u32) (param "b" u32) (result (option u32)) (canon lift (core func $i "pass1") (memory $i "memory") ) ) - (func (export "to-option-string") (param u32) (param string) (result (option string)) + (func (export "to-option-string") (param "a" u32) (param "b" string) (result (option string)) (canon lift (core func $i "pass2") (memory $i "memory") @@ -1488,10 +1497,10 @@ fn option() -> Result<()> { let linker = Linker::new(&engine); let instance = linker.instantiate(&mut store, &component)?; let option_unit_to_u32 = - instance.get_typed_func::<(Option<()>,), u32, _>(&mut store, "option-unit-to-u32")?; - assert_eq!(option_unit_to_u32.call(&mut store, (None,))?, 0); + instance.get_typed_func::<(Option<()>,), (u32,), _>(&mut store, "option-unit-to-u32")?; + assert_eq!(option_unit_to_u32.call(&mut store, (None,))?, (0,)); option_unit_to_u32.post_return(&mut store)?; - assert_eq!(option_unit_to_u32.call(&mut store, (Some(()),))?, 1); + assert_eq!(option_unit_to_u32.call(&mut store, (Some(()),))?, (1,)); option_unit_to_u32.post_return(&mut store)?; let option_u8_to_tuple = instance @@ -1534,49 +1543,49 @@ fn option() -> Result<()> { let instance = linker.instantiate(&mut store, &component)?; let to_option_unit = - instance.get_typed_func::<(u32,), Option<()>, _>(&mut store, "to-option-unit")?; - assert_eq!(to_option_unit.call(&mut store, (0,))?, None); + instance.get_typed_func::<(u32,), (Option<()>,), _>(&mut store, "to-option-unit")?; + assert_eq!(to_option_unit.call(&mut store, (0,))?, (None,)); to_option_unit.post_return(&mut store)?; - assert_eq!(to_option_unit.call(&mut store, (1,))?, Some(())); + assert_eq!(to_option_unit.call(&mut store, (1,))?, (Some(()),)); to_option_unit.post_return(&mut store)?; let err = to_option_unit.call(&mut store, (2,)).unwrap_err(); assert!(err.to_string().contains("invalid option"), "{}", err); let instance = linker.instantiate(&mut store, &component)?; let to_option_u8 = - instance.get_typed_func::<(u32, u32), Option, _>(&mut store, "to-option-u8")?; - assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, None); + instance.get_typed_func::<(u32, u32), (Option,), _>(&mut store, "to-option-u8")?; + assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, (None,)); to_option_u8.post_return(&mut store)?; - assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, Some(0)); + assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, (Some(0),)); to_option_u8.post_return(&mut store)?; - assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, Some(0xfd)); + assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, (Some(0xfd),)); to_option_u8.post_return(&mut store)?; assert!(to_option_u8.call(&mut store, (0x00_02, 0)).is_err()); let instance = linker.instantiate(&mut store, &component)?; let to_option_u32 = - instance.get_typed_func::<(u32, u32), Option, _>(&mut store, "to-option-u32")?; - assert_eq!(to_option_u32.call(&mut store, (0, 0))?, None); + instance.get_typed_func::<(u32, u32), (Option,), _>(&mut store, "to-option-u32")?; + assert_eq!(to_option_u32.call(&mut store, (0, 0))?, (None,)); to_option_u32.post_return(&mut store)?; - assert_eq!(to_option_u32.call(&mut store, (1, 0))?, Some(0)); + assert_eq!(to_option_u32.call(&mut store, (1, 0))?, (Some(0),)); to_option_u32.post_return(&mut store)?; assert_eq!( to_option_u32.call(&mut store, (1, 0x1234fead))?, - Some(0x1234fead) + (Some(0x1234fead),) ); to_option_u32.post_return(&mut store)?; assert!(to_option_u32.call(&mut store, (2, 0)).is_err()); let instance = linker.instantiate(&mut store, &component)?; let to_option_string = instance - .get_typed_func::<(u32, &str), Option, _>(&mut store, "to-option-string")?; - let ret = to_option_string.call(&mut store, (0, ""))?; + .get_typed_func::<(u32, &str), (Option,), _>(&mut store, "to-option-string")?; + let ret = to_option_string.call(&mut store, (0, ""))?.0; assert!(ret.is_none()); to_option_string.post_return(&mut store)?; - let ret = to_option_string.call(&mut store, (1, ""))?; + let ret = to_option_string.call(&mut store, (1, ""))?.0; assert_eq!(ret.unwrap().to_str(&store)?, ""); to_option_string.post_return(&mut store)?; - let ret = to_option_string.call(&mut store, (1, "cheesecake"))?; + let ret = to_option_string.call(&mut store, (1, "cheesecake"))?.0; assert_eq!(ret.unwrap().to_str(&store)?, "cheesecake"); to_option_string.post_return(&mut store)?; assert!(to_option_string.call(&mut store, (2, "")).is_err()); @@ -1637,24 +1646,24 @@ fn expected() -> Result<()> { ) (core instance $i (instantiate $m)) - (func (export "take-expected-unit") (param (expected unit unit)) (result u32) + (func (export "take-expected-unit") (param (result)) (result u32) (canon lift (core func $i "pass0")) ) - (func (export "take-expected-u8-f32") (param (expected u8 float32)) (result (tuple u32 u32)) + (func (export "take-expected-u8-f32") (param (result u8 (error float32))) (result "a" u32) (result "b" u32) (canon lift (core func $i "pass1") (memory $i "memory")) ) (type $list (list u8)) - (func (export "take-expected-string") (param (expected string $list)) (result (tuple u32 string)) + (func (export "take-expected-string") (param (result string (error $list))) (result "a" u32) (result "b" string) (canon lift (core func $i "pass2") (memory $i "memory") (realloc (func $i "realloc")) ) ) - (func (export "to-expected-unit") (param u32) (result (expected unit unit)) + (func (export "to-expected-unit") (param u32) (result (result)) (canon lift (core func $i "pass0")) ) - (func (export "to-expected-s16-f32") (param u32) (param u32) (result (expected s16 float32)) + (func (export "to-expected-s16-f32") (param "a" u32) (param "b" u32) (result (result s16 (error float32))) (canon lift (core func $i "pass1") (memory $i "memory") @@ -1669,11 +1678,11 @@ fn expected() -> Result<()> { let mut store = Store::new(&engine, ()); let linker = Linker::new(&engine); let instance = linker.instantiate(&mut store, &component)?; - let take_expected_unit = - instance.get_typed_func::<(Result<(), ()>,), u32, _>(&mut store, "take-expected-unit")?; - assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, 0); + let take_expected_unit = instance + .get_typed_func::<(Result<(), ()>,), (u32,), _>(&mut store, "take-expected-unit")?; + assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, (0,)); take_expected_unit.post_return(&mut store)?; - assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, 1); + assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, (1,)); take_expected_unit.post_return(&mut store)?; let take_expected_u8_f32 = instance @@ -1702,27 +1711,29 @@ fn expected() -> Result<()> { let instance = linker.instantiate(&mut store, &component)?; let to_expected_unit = - instance.get_typed_func::<(u32,), Result<(), ()>, _>(&mut store, "to-expected-unit")?; - assert_eq!(to_expected_unit.call(&mut store, (0,))?, Ok(())); + instance.get_typed_func::<(u32,), (Result<(), ()>,), _>(&mut store, "to-expected-unit")?; + assert_eq!(to_expected_unit.call(&mut store, (0,))?, (Ok(()),)); to_expected_unit.post_return(&mut store)?; - assert_eq!(to_expected_unit.call(&mut store, (1,))?, Err(())); + assert_eq!(to_expected_unit.call(&mut store, (1,))?, (Err(()),)); to_expected_unit.post_return(&mut store)?; let err = to_expected_unit.call(&mut store, (2,)).unwrap_err(); assert!(err.to_string().contains("invalid expected"), "{}", err); let instance = linker.instantiate(&mut store, &component)?; let to_expected_s16_f32 = instance - .get_typed_func::<(u32, u32), Result, _>(&mut store, "to-expected-s16-f32")?; - assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, Ok(0)); + .get_typed_func::<(u32, u32), (Result,), _>(&mut store, "to-expected-s16-f32")?; + assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, (Ok(0),)); to_expected_s16_f32.post_return(&mut store)?; - assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, Ok(100)); + assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, (Ok(100),)); to_expected_s16_f32.post_return(&mut store)?; assert_eq!( to_expected_s16_f32.call(&mut store, (1, 1.0f32.to_bits()))?, - Err(1.0) + (Err(1.0),) ); to_expected_s16_f32.post_return(&mut store)?; - let ret = to_expected_s16_f32.call(&mut store, (1, CANON_32BIT_NAN | 1))?; + let ret = to_expected_s16_f32 + .call(&mut store, (1, CANON_32BIT_NAN | 1))? + .0; assert_eq!(ret.unwrap_err().to_bits(), CANON_32BIT_NAN); to_expected_s16_f32.post_return(&mut store)?; assert!(to_expected_s16_f32.call(&mut store, (2, 0)).is_err()); @@ -1768,10 +1779,13 @@ fn fancy_list() -> Result<()> { (core instance $i (instantiate $m)) (type $a (option u8)) - (type $b (expected unit string)) + (type $b (result (error string))) (type $input (list (tuple $a $b))) - (type $output (tuple u32 u32 (list u8))) - (func (export "take") (param $input) (result $output) + (func (export "take") + (param $input) + (result "ptr" u32) + (result "len" u32) + (result "list" (list u8)) (canon lift (core func $i "take") (memory $i "memory") @@ -1873,9 +1887,9 @@ fn invalid_alignment() -> Result<()> { (core instance $i (instantiate $m)) (func (export "many-params") - (param string) (param string) (param string) (param string) - (param string) (param string) (param string) (param string) - (param string) (param string) (param string) (param string) + (param "s1" string) (param "s2" string) (param "s3" string) (param "s4" string) + (param "s5" string) (param "s6" string) (param "s7" string) (param "s8" string) + (param "s9" string) (param "s10" string) (param "s11" string) (param "s12" string) (canon lift (core func $i "take-i32") (memory $i "memory") @@ -1929,7 +1943,7 @@ fn invalid_alignment() -> Result<()> { ); let err = instance(&mut store)? - .get_typed_func::<(), WasmStr, _>(&mut store, "string-ret")? + .get_typed_func::<(), (WasmStr,), _>(&mut store, "string-ret")? .call(&mut store, ()) .err() .unwrap(); @@ -1940,7 +1954,7 @@ fn invalid_alignment() -> Result<()> { ); let err = instance(&mut store)? - .get_typed_func::<(), WasmList, _>(&mut store, "list-u32-ret")? + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u32-ret")? .call(&mut store, ()) .err() .unwrap(); @@ -2073,8 +2087,9 @@ fn raw_slice_of_various_types() -> Result<()> { }; let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-u8")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u8")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2083,8 +2098,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-i8")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-i8")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2094,8 +2110,9 @@ fn raw_slice_of_various_types() -> Result<()> { ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-u16")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u16")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2110,8 +2127,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-i16")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-i16")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2126,8 +2144,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-u32")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u32")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2138,8 +2157,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-i32")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-i32")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2150,8 +2170,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-u64")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-u64")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2160,8 +2181,9 @@ fn raw_slice_of_various_types() -> Result<()> { ] ); let list = instance - .get_typed_func::<(), WasmList, _>(&mut store, "list-i64")? - .call_and_post_return(&mut store, ())?; + .get_typed_func::<(), (WasmList,), _>(&mut store, "list-i64")? + .call_and_post_return(&mut store, ())? + .0; assert_eq!( list.as_le_slice(&store), [ @@ -2194,11 +2216,11 @@ fn lower_then_lift() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); let mut linker = Linker::new(&engine); - linker.root().func_wrap("f", || Ok(2u32))?; + linker.root().func_wrap("f", || Ok((2u32,)))?; let instance = linker.instantiate(&mut store, &component)?; - let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?; - assert_eq!(f.call(&mut store, ())?, 2); + let f = instance.get_typed_func::<(), (i32,), _>(&mut store, "f")?; + assert_eq!(f.call(&mut store, ())?, (2,)); // First test strings when the import/export ABI happen to line up let component = format!( @@ -2272,11 +2294,11 @@ fn lower_then_lift() -> Result<()> { .root() .func_wrap("s2", |store: StoreContextMut<'_, ()>, x: WasmStr| { assert_eq!(x.to_str(&store)?, "hello"); - Ok(u32::MAX) + Ok((u32::MAX,)) })?; let instance = linker.instantiate(&mut store, &component)?; - let f = instance.get_typed_func::<(&str,), WasmStr, _>(&mut store, "f")?; + let f = instance.get_typed_func::<(&str,), (WasmStr,), _>(&mut store, "f")?; let err = f.call(&mut store, ("hello",)).err().unwrap(); assert!( err.to_string().contains("return pointer not aligned"), @@ -2339,7 +2361,7 @@ fn errors_that_poison_instance() -> Result<()> { assert_poisoned(f3.call(&mut store, ("x",))); let instance = linker.instantiate(&mut store, &component)?; - let f4 = instance.get_typed_func::<(), WasmStr, _>(&mut store, "f4")?; + let f4 = instance.get_typed_func::<(), (WasmStr,), _>(&mut store, "f4")?; assert!(f4.call(&mut store, ()).is_err()); assert_poisoned(f4.call(&mut store, ())); @@ -2414,7 +2436,7 @@ fn run_export_with_internal_adapter() -> Result<()> { let mut store = Store::new(&engine, ()); let linker = Linker::new(&engine); let instance = linker.instantiate(&mut store, &component)?; - let run = instance.get_typed_func::<(), u32, _>(&mut store, "run")?; - assert_eq!(run.call(&mut store, ())?, 5); + let run = instance.get_typed_func::<(), (u32,), _>(&mut store, "run")?; + assert_eq!(run.call(&mut store, ())?, (5,)); Ok(()) } diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index 5bba4fcfe016..145063dc915a 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -147,11 +147,11 @@ fn simple() -> Result<()> { linker.root().func_new( &component, "", - |mut store: StoreContextMut<'_, Option>, args| { + |mut store: StoreContextMut<'_, Option>, args, _results| { if let Val::String(s) = &args[0] { assert!(store.data().is_none()); *store.data_mut() = Some(s.to_string()); - Ok(Val::Unit) + Ok(()) } else { panic!() } @@ -161,7 +161,7 @@ fn simple() -> Result<()> { instance .get_func(&mut store, "call") .unwrap() - .call(&mut store, &[])?; + .call(&mut store, &[], &mut [])?; assert_eq!(store.data().as_ref().unwrap(), "hello world"); Ok(()) @@ -244,9 +244,7 @@ fn attempt_to_leave_during_malloc() -> Result<()> { .func_wrap("thunk", || -> Result<()> { panic!("should not get here") })?; linker .root() - .func_wrap("ret-string", || -> Result { - Ok("hello".to_string()) - })?; + .func_wrap("ret-string", || -> Result<_> { Ok(("hello".to_string(),)) })?; let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, ()); @@ -368,22 +366,22 @@ fn attempt_to_reenter_during_host() -> Result<()> { linker.root().func_new( &component, "thunk", - |mut store: StoreContextMut<'_, DynamicState>, _| { + |mut store: StoreContextMut<'_, DynamicState>, _, _| { let func = store.data_mut().func.take().unwrap(); - let trap = func.call(&mut store, &[]).unwrap_err(); + let trap = func.call(&mut store, &[], &mut []).unwrap_err(); assert!( trap.to_string() .contains("cannot reenter component instance"), "bad trap: {}", trap, ); - Ok(Val::Unit) + Ok(()) }, )?; let instance = linker.instantiate(&mut store, &component)?; let func = instance.get_func(&mut store, "run").unwrap(); store.data_mut().func = Some(func); - func.call(&mut store, &[])?; + func.call(&mut store, &[], &mut [])?; Ok(()) } @@ -532,9 +530,9 @@ fn stack_and_heap_args_and_rets() -> Result<()> { // First, test the static API let mut linker = Linker::new(&engine); - linker.root().func_wrap("f1", |x: u32| -> Result { + linker.root().func_wrap("f1", |x: u32| -> Result<(u32,)> { assert_eq!(x, 1); - Ok(2) + Ok((2,)) })?; linker.root().func_wrap( "f2", @@ -550,16 +548,16 @@ fn stack_and_heap_args_and_rets() -> Result<()> { WasmStr, WasmStr, )| - -> Result { + -> Result<(u32,)> { assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); - Ok(3) + Ok((3,)) }, )?; linker .root() - .func_wrap("f3", |arg: u32| -> Result { + .func_wrap("f3", |arg: u32| -> Result<(String,)> { assert_eq!(arg, 8); - Ok("xyz".to_string()) + Ok(("xyz".to_string(),)) })?; linker.root().func_wrap( "f4", @@ -575,9 +573,9 @@ fn stack_and_heap_args_and_rets() -> Result<()> { WasmStr, WasmStr, )| - -> Result { + -> Result<(String,)> { assert_eq!(arg.0.to_str(&cx).unwrap(), "abc"); - Ok("xyz".to_string()) + Ok(("xyz".to_string(),)) }, )?; let instance = linker.instantiate(&mut store, &component)?; @@ -588,51 +586,63 @@ fn stack_and_heap_args_and_rets() -> Result<()> { // Next, test the dynamic API let mut linker = Linker::new(&engine); - linker.root().func_new(&component, "f1", |_, args| { - if let Val::U32(x) = &args[0] { - assert_eq!(*x, 1); - Ok(Val::U32(2)) - } else { - panic!() - } - })?; - linker.root().func_new(&component, "f2", |_, args| { - if let Val::Tuple(tuple) = &args[0] { - if let Val::String(s) = &tuple.values()[0] { - assert_eq!(s.deref(), "abc"); - Ok(Val::U32(3)) + linker + .root() + .func_new(&component, "f1", |_, args, results| { + if let Val::U32(x) = &args[0] { + assert_eq!(*x, 1); + results[0] = Val::U32(2); + Ok(()) } else { panic!() } - } else { - panic!() - } - })?; - linker.root().func_new(&component, "f3", |_, args| { - if let Val::U32(x) = &args[0] { - assert_eq!(*x, 8); - Ok(Val::String("xyz".into())) - } else { - panic!(); - } - })?; - linker.root().func_new(&component, "f4", |_, args| { - if let Val::Tuple(tuple) = &args[0] { - if let Val::String(s) = &tuple.values()[0] { - assert_eq!(s.deref(), "abc"); - Ok(Val::String("xyz".into())) + })?; + linker + .root() + .func_new(&component, "f2", |_, args, results| { + if let Val::Tuple(tuple) = &args[0] { + if let Val::String(s) = &tuple.values()[0] { + assert_eq!(s.deref(), "abc"); + results[0] = Val::U32(3); + Ok(()) + } else { + panic!() + } } else { panic!() } - } else { - panic!() - } - })?; + })?; + linker + .root() + .func_new(&component, "f3", |_, args, results| { + if let Val::U32(x) = &args[0] { + assert_eq!(*x, 8); + results[0] = Val::String("xyz".into()); + Ok(()) + } else { + panic!(); + } + })?; + linker + .root() + .func_new(&component, "f4", |_, args, results| { + if let Val::Tuple(tuple) = &args[0] { + if let Val::String(s) = &tuple.values()[0] { + assert_eq!(s.deref(), "abc"); + results[0] = Val::String("xyz".into()); + Ok(()) + } else { + panic!() + } + } else { + panic!() + } + })?; let instance = linker.instantiate(&mut store, &component)?; instance .get_func(&mut store, "run") .unwrap() - .call(&mut store, &[])?; + .call(&mut store, &[], &mut [])?; Ok(()) } @@ -693,8 +703,8 @@ fn bad_import_alignment() -> Result<()> { let mut linker = Linker::new(&engine); linker .root() - .func_wrap("unaligned-retptr", || -> Result { - Ok(String::new()) + .func_wrap("unaligned-retptr", || -> Result<(String,)> { + Ok((String::new(),)) })?; linker.root().func_wrap( "unaligned-argptr", @@ -783,18 +793,20 @@ fn no_actual_wasm_code() -> Result<()> { *store.data_mut() = 0; let mut linker = Linker::new(&engine); - linker - .root() - .func_new(&component, "f", |mut store: StoreContextMut<'_, u32>, _| { + linker.root().func_new( + &component, + "f", + |mut store: StoreContextMut<'_, u32>, _, _| { *store.data_mut() += 1; - Ok(Val::Unit) - })?; + Ok(()) + }, + )?; let instance = linker.instantiate(&mut store, &component)?; let thunk = instance.get_func(&mut store, "thunk").unwrap(); assert_eq!(*store.data(), 0); - thunk.call(&mut store, &[])?; + thunk.call(&mut store, &[], &mut [])?; assert_eq!(*store.data(), 1); Ok(()) diff --git a/tests/all/component_model/macros.rs b/tests/all/component_model/macros.rs index 190d6062c772..4ca5851dd213 100644 --- a/tests/all/component_model/macros.rs +++ b/tests/all/component_model/macros.rs @@ -27,10 +27,10 @@ fn record_derive() -> Result<()> { let input = Foo { a: -42, b: 73 }; let output = instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")? + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")? .call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); // Sad path: field count mismatch (too few) @@ -41,7 +41,7 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: field count mismatch (too many) @@ -56,7 +56,7 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: field name mismatch @@ -68,7 +68,7 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: field type mismatch @@ -80,7 +80,7 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with generics this time @@ -105,10 +105,10 @@ fn record_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let output = instance - .get_typed_func::<(Generic,), Generic, _>(&mut store, "echo")? + .get_typed_func::<(Generic,), (Generic,), _>(&mut store, "echo")? .call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); Ok(()) } @@ -130,12 +130,12 @@ fn union_derive() -> Result<()> { let component = Component::new(&engine, make_echo_component("(union s32 u32 s32)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")?; for &input in &[Foo::A(-42), Foo::B(73), Foo::C(314159265)] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Sad path: case count mismatch (too few) @@ -144,7 +144,7 @@ fn union_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case count mismatch (too many) @@ -156,11 +156,11 @@ fn union_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case type mismatch @@ -169,7 +169,7 @@ fn union_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with generics this time @@ -184,7 +184,7 @@ fn union_derive() -> Result<()> { let component = Component::new(&engine, make_echo_component("(union s32 u32 s32)", 8))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Generic,), Generic, _>( + let func = instance.get_typed_func::<(Generic,), (Generic,), _>( &mut store, "echo", )?; @@ -195,7 +195,7 @@ fn union_derive() -> Result<()> { ] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } Ok(()) @@ -220,17 +220,17 @@ fn variant_derive() -> Result<()> { let component = Component::new( &engine, make_echo_component( - r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit))"#, + r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C"))"#, 8, ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")?; for &input in &[Foo::A(-42), Foo::B(73), Foo::C] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Sad path: case count mismatch (too few) @@ -242,7 +242,7 @@ fn variant_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case count mismatch (too many) @@ -250,29 +250,26 @@ fn variant_derive() -> Result<()> { let component = Component::new( &engine, make_echo_component( - r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit) (case "D" u32))"#, + r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C") (case "D" u32))"#, 8, ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case name mismatch let component = Component::new( &engine, - make_echo_component( - r#"(variant (case "A" s32) (case "B" u32) (case "C" unit))"#, - 8, - ), + make_echo_component(r#"(variant (case "A" s32) (case "B" u32) (case "C"))"#, 8), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case type mismatch @@ -280,14 +277,14 @@ fn variant_derive() -> Result<()> { let component = Component::new( &engine, make_echo_component( - r#"(variant (case "foo-bar-baz" s32) (case "B" s32) (case "C" unit))"#, + r#"(variant (case "foo-bar-baz" s32) (case "B" s32) (case "C"))"#, 8, ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with generics this time @@ -304,18 +301,18 @@ fn variant_derive() -> Result<()> { let component = Component::new( &engine, make_echo_component( - r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C" unit))"#, + r#"(variant (case "foo-bar-baz" s32) (case "B" u32) (case "C"))"#, 8, ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; let func = instance - .get_typed_func::<(Generic,), Generic, _>(&mut store, "echo")?; + .get_typed_func::<(Generic,), (Generic,), _>(&mut store, "echo")?; for &input in &[Generic::::A(-42), Generic::B(73), Generic::C] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } Ok(()) @@ -342,12 +339,12 @@ fn enum_derive() -> Result<()> { make_echo_component(r#"(enum "foo-bar-baz" "B" "C")"#, 4), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")?; for &input in &[Foo::A, Foo::B, Foo::C] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Sad path: case count mismatch (too few) @@ -359,7 +356,7 @@ fn enum_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case count mismatch (too many) @@ -371,7 +368,7 @@ fn enum_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: case name mismatch @@ -380,7 +377,7 @@ fn enum_derive() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with large enums (i.e. more than 2^8 cases) @@ -404,12 +401,12 @@ fn enum_derive() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Many,), Many, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Many,), (Many,), _>(&mut store, "echo")?; for &input in &[Many::V0, Many::V1, Many::V254, Many::V255, Many::V256] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // TODO: The following case takes forever (i.e. I gave up after 30 minutes) to compile; we'll need to profile @@ -437,9 +434,9 @@ fn flags() -> Result<()> { let component = Component::new(&engine, make_echo_component(r#"(flags)"#, 0))?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Flags0,), Flags0, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Flags0,), (Flags0,), _>(&mut store, "echo")?; let output = func.call_and_post_return(&mut store, (Flags0::default(),))?; - assert_eq!(output, Flags0::default()); + assert_eq!(output, (Flags0::default(),)); // Simple 8-bit flags wasmtime::component::flags! { @@ -465,7 +462,7 @@ fn flags() -> Result<()> { make_echo_component(r#"(flags "foo-bar-baz" "B" "C")"#, 4), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo,), Foo, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo")?; for n in 0..8 { let mut input = Foo::default(); @@ -481,7 +478,7 @@ fn flags() -> Result<()> { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Sad path: flag count mismatch (too few) @@ -493,7 +490,7 @@ fn flags() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: flag count mismatch (too many) @@ -505,7 +502,7 @@ fn flags() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Sad path: flag name mismatch @@ -514,7 +511,7 @@ fn flags() -> Result<()> { let instance = Linker::new(&engine).instantiate(&mut store, &component)?; assert!(instance - .get_typed_func::<(Foo,), Foo, _>(&mut store, "echo") + .get_typed_func::<(Foo,), (Foo,), _>(&mut store, "echo") .is_err()); // Happy path redux, with large flag count (exactly 8) @@ -560,7 +557,7 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo8Exact,), Foo8Exact, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo8Exact,), (Foo8Exact,), _>(&mut store, "echo")?; for &input in &[ Foo8Exact::F0, @@ -571,7 +568,7 @@ fn flags() -> Result<()> { ] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (more than 8) @@ -609,12 +606,12 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo16,), Foo16, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo16,), (Foo16,), _>(&mut store, "echo")?; for &input in &[Foo16::F0, Foo16::F1, Foo16::F6, Foo16::F7, Foo16::F8] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (exactly 16) @@ -657,7 +654,7 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo16Exact,), Foo16Exact, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo16Exact,), (Foo16Exact,), _>(&mut store, "echo")?; for &input in &[ Foo16Exact::F0, @@ -668,7 +665,7 @@ fn flags() -> Result<()> { ] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (more than 16) @@ -696,12 +693,12 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo32,), Foo32, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo32,), (Foo32,), _>(&mut store, "echo")?; for &input in &[Foo32::F0, Foo32::F1, Foo32::F14, Foo32::F15, Foo32::F16] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (exactly 32) @@ -744,7 +741,7 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo32Exact,), Foo32Exact, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo32Exact,), (Foo32Exact,), _>(&mut store, "echo")?; for &input in &[ Foo32Exact::F0, @@ -755,7 +752,7 @@ fn flags() -> Result<()> { ] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (more than 32) @@ -783,12 +780,12 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo64,), Foo64, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo64,), (Foo64,), _>(&mut store, "echo")?; for &input in &[Foo64::F0, Foo64::F1, Foo64::F30, Foo64::F31, Foo64::F32] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } // Happy path redux, with large flag count (more than 64) @@ -816,12 +813,12 @@ fn flags() -> Result<()> { ), )?; let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(Foo96,), Foo96, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(Foo96,), (Foo96,), _>(&mut store, "echo")?; for &input in &[Foo96::F0, Foo96::F1, Foo96::F62, Foo96::F63, Foo96::F64] { let output = func.call_and_post_return(&mut store, (input,))?; - assert_eq!(input, output); + assert_eq!((input,), output); } Ok(()) diff --git a/tests/all/component_model/nested.rs b/tests/all/component_model/nested.rs index acc04a2048bc..ee40c92862e5 100644 --- a/tests/all/component_model/nested.rs +++ b/tests/all/component_model/nested.rs @@ -162,11 +162,12 @@ fn thread_options_through_inner() -> Result<()> { let mut linker = Linker::new(&engine); linker .root() - .func_wrap("hostfn", |param: u32| Ok(param.to_string()))?; + .func_wrap("hostfn", |param: u32| Ok((param.to_string(),)))?; let instance = linker.instantiate(&mut store, &component)?; let result = instance - .get_typed_func::<(u32,), WasmStr, _>(&mut store, "run")? - .call(&mut store, (43,))?; + .get_typed_func::<(u32,), (WasmStr,), _>(&mut store, "run")? + .call(&mut store, (43,))? + .0; assert_eq!(result.to_str(&store)?, "42"); Ok(()) } diff --git a/tests/all/component_model/post_return.rs b/tests/all/component_model/post_return.rs index b0b74f15bb1f..b301567c6f41 100644 --- a/tests/all/component_model/post_return.rs +++ b/tests/all/component_model/post_return.rs @@ -195,21 +195,21 @@ fn post_return_all_types() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, false); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let i32 = instance.get_typed_func::<(), u32, _>(&mut store, "i32")?; - let i64 = instance.get_typed_func::<(), u64, _>(&mut store, "i64")?; - let f32 = instance.get_typed_func::<(), f32, _>(&mut store, "f32")?; - let f64 = instance.get_typed_func::<(), f64, _>(&mut store, "f64")?; + let i32 = instance.get_typed_func::<(), (u32,), _>(&mut store, "i32")?; + let i64 = instance.get_typed_func::<(), (u64,), _>(&mut store, "i64")?; + let f32 = instance.get_typed_func::<(), (f32,), _>(&mut store, "f32")?; + let f64 = instance.get_typed_func::<(), (f64,), _>(&mut store, "f64")?; - assert_eq!(i32.call(&mut store, ())?, 1); + assert_eq!(i32.call(&mut store, ())?, (1,)); i32.post_return(&mut store)?; - assert_eq!(i64.call(&mut store, ())?, 2); + assert_eq!(i64.call(&mut store, ())?, (2,)); i64.post_return(&mut store)?; - assert_eq!(f32.call(&mut store, ())?, 3.); + assert_eq!(f32.call(&mut store, ())?, (3.,)); f32.post_return(&mut store)?; - assert_eq!(f64.call(&mut store, ())?, 4.); + assert_eq!(f64.call(&mut store, ())?, (4.,)); f64.post_return(&mut store)?; Ok(()) @@ -250,8 +250,8 @@ fn post_return_string() -> Result<()> { let component = Component::new(&engine, component)?; let mut store = Store::new(&engine, false); let instance = Linker::new(&engine).instantiate(&mut store, &component)?; - let get = instance.get_typed_func::<(), WasmStr, _>(&mut store, "get")?; - let s = get.call(&mut store, ())?; + let get = instance.get_typed_func::<(), (WasmStr,), _>(&mut store, "get")?; + let s = get.call(&mut store, ())?.0; assert_eq!(s.to_str(&store)?, "hello world"); get.post_return(&mut store)?; diff --git a/tests/all/component_model/strings.rs b/tests/all/component_model/strings.rs index 59997090deba..2d4f233a684b 100644 --- a/tests/all/component_model/strings.rs +++ b/tests/all/component_model/strings.rs @@ -175,15 +175,15 @@ fn test_roundtrip(engine: &Engine, src: &str, dst: &str) -> Result<()> { .root() .func_wrap("host", |store: StoreContextMut, arg: String| { assert_eq!(*store.data(), arg); - Ok(arg) + Ok((arg,)) })?; let instance = linker.instantiate(&mut store, &component)?; - let func = instance.get_typed_func::<(String,), String, _>(&mut store, "echo")?; + let func = instance.get_typed_func::<(String,), (String,), _>(&mut store, "echo")?; for string in STRINGS { println!("testing string {string:?}"); *store.data_mut() = string.to_string(); - let ret = func.call(&mut store, (string.to_string(),))?; + let (ret,) = func.call(&mut store, (string.to_string(),))?; assert_eq!(ret, *string); func.post_return(&mut store)?; } @@ -554,7 +554,7 @@ fn test_raw_when_encoded( (func (export "f") (param i32 i32 i32) (call $f (local.get 0) (local.get 2))) ) (core instance $m (instantiate $m (with "" (instance (export "" (func $f)))))) - (func (export "f") (param (list u8)) (param u32) (canon lift (core func $m "f") + (func (export "f") (param "a" (list u8)) (param "b" u32) (canon lift (core func $m "f") (memory $libc "memory") (realloc (func $libc "realloc")))) ) diff --git a/tests/misc_testsuite/component-model/adapter.wast b/tests/misc_testsuite/component-model/adapter.wast index 69d73620de5d..4acf0a5a7097 100644 --- a/tests/misc_testsuite/component-model/adapter.wast +++ b/tests/misc_testsuite/component-model/adapter.wast @@ -121,7 +121,7 @@ (memory (export "memory") 0) ) (core instance $m (instantiate $m)) - (func $f (param (list unit)) + (func $f (param (list (record))) (canon lift (core func $m "x") (realloc (func $m "realloc")) diff --git a/tests/misc_testsuite/component-model/fused.wast b/tests/misc_testsuite/component-model/fused.wast index 77dca93edc3d..53981c1bdd4c 100644 --- a/tests/misc_testsuite/component-model/fused.wast +++ b/tests/misc_testsuite/component-model/fused.wast @@ -94,10 +94,10 @@ (component (type $roundtrip (func ;; 20 u32 params - (param u32) (param u32) (param u32) (param u32) (param u32) - (param u32) (param u32) (param u32) (param u32) (param u32) - (param u32) (param u32) (param u32) (param u32) (param u32) - (param u32) (param u32) (param u32) (param u32) (param u32) + (param "a1" u32) (param "a2" u32) (param "a3" u32) (param "a4" u32) (param "a5" u32) + (param "a6" u32) (param "a7" u32) (param "a8" u32) (param "a9" u32) (param "a10" u32) + (param "a11" u32) (param "a12" u32) (param "a13" u32) (param "a14" u32) (param "a15" u32) + (param "a16" u32) (param "a17" u32) (param "a18" u32) (param "a19" u32) (param "a20" u32) ;; 10 u32 results (result (tuple u32 u32 u32 u32 u32 u32 u32 u32 u32 u32)) @@ -717,8 +717,8 @@ ;; simple variant translation (component - (type $a (variant (case "x" unit))) - (type $b (variant (case "y" unit))) + (type $a (variant (case "x"))) + (type $b (variant (case "y"))) (component $c1 (core module $m @@ -756,7 +756,7 @@ ;; invalid variant discriminant in a parameter (assert_trap (component - (type $a (variant (case "x" unit))) + (type $a (variant (case "x"))) (component $c1 (core module $m @@ -789,7 +789,7 @@ ;; invalid variant discriminant in a result (assert_trap (component - (type $a (variant (case "x" unit))) + (type $a (variant (case "x"))) (component $c1 (core module $m @@ -889,6 +889,12 @@ (type $d (variant (case "a" float32) (case "b" float64))) (type $e (variant (case "a" float32) (case "b" s64))) + (type $func_a (func (param "x" bool) (param "a" $a))) + (type $func_b (func (param "x" bool) (param "b" $b))) + (type $func_c (func (param "x" bool) (param "c" $c))) + (type $func_d (func (param "x" bool) (param "d" $d))) + (type $func_e (func (param "x" bool) (param "e" $d))) + (component $c1 (core module $m (func (export "a") (param i32 i32 i32) @@ -943,19 +949,19 @@ ) ) (core instance $m (instantiate $m)) - (func (export "a") (param bool) (param $a) (canon lift (core func $m "a"))) - (func (export "b") (param bool) (param $b) (canon lift (core func $m "b"))) - (func (export "c") (param bool) (param $c) (canon lift (core func $m "c"))) - (func (export "d") (param bool) (param $d) (canon lift (core func $m "d"))) - (func (export "e") (param bool) (param $e) (canon lift (core func $m "e"))) + (func (export "a") (type $func_a) (canon lift (core func $m "a"))) + (func (export "b") (type $func_b) (canon lift (core func $m "b"))) + (func (export "c") (type $func_c) (canon lift (core func $m "c"))) + (func (export "d") (type $func_d) (canon lift (core func $m "d"))) + (func (export "e") (type $func_e) (canon lift (core func $m "e"))) ) (component $c2 (import "" (instance $i - (export "a" (func (param bool) (param $a))) - (export "b" (func (param bool) (param $b))) - (export "c" (func (param bool) (param $c))) - (export "d" (func (param bool) (param $d))) - (export "e" (func (param bool) (param $e))) + (export "a" (func (type $func_a))) + (export "b" (func (type $func_b))) + (export "c" (func (type $func_c))) + (export "d" (func (type $func_d))) + (export "e" (func (type $func_e))) )) (core func $a (canon lower (func $i "a"))) @@ -1008,10 +1014,10 @@ ;; different size variants (component (type $a (variant - (case "a" unit) + (case "a") (case "b" float32) (case "c" (tuple float32 u32)) - (case "d" (tuple float32 unit u64 u8)) + (case "d" (tuple float32 (record) u64 u8)) )) (component $c1 @@ -1054,11 +1060,11 @@ ) ) (core instance $m (instantiate $m)) - (func (export "a") (param u8) (param $a) (canon lift (core func $m "a"))) + (func (export "a") (param "x" u8) (param "a" $a) (canon lift (core func $m "a"))) ) (component $c2 (import "" (instance $i - (export "a" (func (param u8) (param $a))) + (export "a" (func (param "x" u8) (param "a" $a))) )) (core func $a (canon lower (func $i "a"))) @@ -1157,9 +1163,9 @@ (export "roundtrip" (func $c2 "roundtrip")) ) -(assert_return (invoke "roundtrip" (char.const "x")) (unit.const)) -(assert_return (invoke "roundtrip" (char.const "⛳")) (unit.const)) -(assert_return (invoke "roundtrip" (char.const "🍰")) (unit.const)) +(assert_return (invoke "roundtrip" (char.const "x"))) +(assert_return (invoke "roundtrip" (char.const "⛳"))) +(assert_return (invoke "roundtrip" (char.const "🍰"))) ;; invalid chars (assert_trap diff --git a/tests/misc_testsuite/component-model/simple.wast b/tests/misc_testsuite/component-model/simple.wast index df1c05b53690..df4088b94e7a 100644 --- a/tests/misc_testsuite/component-model/simple.wast +++ b/tests/misc_testsuite/component-model/simple.wast @@ -39,4 +39,4 @@ (func (export "") (canon lift (core func $m ""))) ) -(assert_return (invoke "") (unit.const)) +(assert_return (invoke "")) diff --git a/tests/misc_testsuite/component-model/types.wast b/tests/misc_testsuite/component-model/types.wast index 91d75c4a75b0..eef65f7aa865 100644 --- a/tests/misc_testsuite/component-model/types.wast +++ b/tests/misc_testsuite/component-model/types.wast @@ -1,9 +1,12 @@ (component (type string) (type (func (param string))) - (type $r (record (field "x" unit) (field "y" string))) + (type $r (record (field "x" (record)) (field "y" string))) (type $u (union $r string)) - (type $e (expected $u u32)) + (type $e (result $u (error u32))) + (type (result $u)) + (type (result (error $u))) + (type (result)) (type (func (param $e) (result (option $r)))) @@ -21,17 +24,17 @@ ;; primitives in functions (type (func - (param bool) - (param u8) - (param s8) - (param u16) - (param s16) - (param u32) - (param s32) - (param u64) - (param s64) - (param char) - (param string) + (param "a" bool) + (param "b" u8) + (param "c" s8) + (param "d" u16) + (param "e" s16) + (param "f" u32) + (param "g" s32) + (param "h" u64) + (param "i" s64) + (param "j" char) + (param "k" string) )) ;; primitives in types diff --git a/tests/misc_testsuite/threads/load-store-alignment.wast b/tests/misc_testsuite/threads/load-store-alignment.wast index 5d8fff8db7ff..f95a8531bf19 100644 --- a/tests/misc_testsuite/threads/load-store-alignment.wast +++ b/tests/misc_testsuite/threads/load-store-alignment.wast @@ -110,17 +110,17 @@ (assert_return (invoke "64.store64 o1" (i32.const 7))) ;; misaligned stores -(assert_return (invoke "32.store8" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "32.store8" (i32.const 1))) (assert_trap (invoke "32.store16" (i32.const 1)) "misaligned memory access") (assert_trap (invoke "32.store32" (i32.const 1)) "misaligned memory access") -(assert_return (invoke "64.store8" (i32.const 1)) (i64.const 0)) +(assert_return (invoke "64.store8" (i32.const 1))) (assert_trap (invoke "64.store16" (i32.const 1)) "misaligned memory access") (assert_trap (invoke "64.store32" (i32.const 1)) "misaligned memory access") (assert_trap (invoke "64.store64" (i32.const 1)) "misaligned memory access") -(assert_return (invoke "32.store8 o1" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "32.store8 o1" (i32.const 0))) (assert_trap (invoke "32.store16 o1" (i32.const 0)) "misaligned memory access") (assert_trap (invoke "32.store32 o1" (i32.const 0)) "misaligned memory access") -(assert_return (invoke "64.store8 o1" (i32.const 0)) (i64.const 0)) +(assert_return (invoke "64.store8 o1" (i32.const 0))) (assert_trap (invoke "64.store16 o1" (i32.const 0)) "misaligned memory access") (assert_trap (invoke "64.store32 o1" (i32.const 0)) "misaligned memory access") (assert_trap (invoke "64.store64 o1" (i32.const 0)) "misaligned memory access")